Index: /pjproject/trunk/build.mak.in
===================================================================
--- /pjproject/trunk/build.mak.in	(revision 4703)
+++ /pjproject/trunk/build.mak.in	(revision 4704)
@@ -173,4 +173,11 @@
 	-I$(PJDIR)/pjsip/include
 export APP_CXXFLAGS := $(APP_CFLAGS)
+#  x   x  x  x  x  x  x   x  x  x  x  x  x   x  x  x  x  x  x   x  x  x  x  x
+#
+# FIX THIS
+#
+# pjsua2 is c++ library hence maybe needs to be put in separate
+# variables. it will also require -lstdc++ or -static-libstdc++
+#  x   x  x  x  x  x  x   x  x  x  x  x  x   x  x  x  x  x  x   x  x  x  x  x
 export APP_LDFLAGS := -L$(PJDIR)/pjlib/lib\
 	-L$(PJDIR)/pjlib-util/lib\
@@ -180,6 +187,17 @@
 	-L$(PJDIR)/third_party/lib\
 	$(PJ_VIDEO_LDFLAGS) \
+	-static-libstdc++ \
 	@LDFLAGS@
-export APP_LIB_FILES = $(PJ_DIR)/pjsip/lib/libpjsua-$(LIB_SUFFIX) \
+
+#  x   x  x  x  x  x  x   x  x  x  x  x  x   x  x  x  x  x  x   x  x  x  x  x
+#
+# FIX THIS
+#
+# pjsua2 is c++ library hence maybe needs to be put in separate
+# variables. it will also require -lstdc++
+#  x   x  x  x  x  x  x   x  x  x  x  x  x   x  x  x  x  x  x   x  x  x  x  x
+export APP_LIB_FILES = \
+	$(PJ_DIR)/pjsip/lib/libpjsua2-$(LIB_SUFFIX) \
+	$(PJ_DIR)/pjsip/lib/libpjsua-$(LIB_SUFFIX) \
 	$(PJ_DIR)/pjsip/lib/libpjsip-ua-$(LIB_SUFFIX) \
 	$(PJ_DIR)/pjsip/lib/libpjsip-simple-$(LIB_SUFFIX) \
Index: /pjproject/trunk/configure-android
===================================================================
--- /pjproject/trunk/configure-android	(revision 4703)
+++ /pjproject/trunk/configure-android	(revision 4704)
@@ -102,6 +102,6 @@
   export LIBS="${LIBS} -lc -lgcc"
   export CFLAGS="${NDK_CFLAGS} ${CFLAGS}"
-  export CPPFLAGS="${CFLAGS}"
-  export CXXFLAGS="${NDK_CXXFLAGS}"
+  export CPPFLAGS="${CFLAGS} -fexceptions -frtti"
+  export CXXFLAGS="${NDK_CXXFLAGS} -fexceptions -frtti"
 
 else
@@ -128,8 +128,26 @@
   export LIBS="${LIBS} -lc -lgcc"
   export CFLAGS="${CFLAGS} -I${ANDROID_SYSROOT}/usr/include"
-  export CPPFLAGS="${CFLAGS}"
-  export CXXFLAGS="${CXXFLAGS} -shared --sysroot=${ANDROID_SYSROOT}"
+  export CPPFLAGS="${CFLAGS} -fexceptions -frtti"
+  export CXXFLAGS="${CXXFLAGS} -shared --sysroot=${ANDROID_SYSROOT} -fexceptions -frtti"
 
 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/armeabi/include"
+STDCPP_LIBS="${ANDROID_SYSROOT}/usr/lib/crtbegin_so.o -lgnustl_static"
+STDCPP_LDFLAGS="-L${STDCPP_TC_VER}/libs/armeabi"
+
+# stlport
+#STDCPP_CFLAGS="-I${ANDROID_NDK_ROOT}/sources/cxx-stl/stlport/stlport"
+#STDCPP_LIBS="${ANDROID_SYSROOT}/usr/lib/crtbegin_so.o -lstlport_static -ldl"
+#STDCPP_LDFLAGS="-L${ANDROID_NDK_ROOT}/sources/cxx-stl/stlport/libs/armeabi"
+
+export CFLAGS="${CFLAGS} ${STDCPP_CFLAGS}"
+export LIBS="${STDCPP_LIBS} ${LIBS}"
+export LDFLAGS="${LDFLAGS} ${STDCPP_LDFLAGS}"
 
 # Print settings
Index: /pjproject/trunk/doc/pjsip-book/Doxyfile
===================================================================
--- /pjproject/trunk/doc/pjsip-book/Doxyfile	(revision 4704)
+++ /pjproject/trunk/doc/pjsip-book/Doxyfile	(revision 4704)
@@ -0,0 +1,1781 @@
+# Doxyfile 1.7.6.1
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project.
+#
+# All text after a hash (#) is considered a comment and will be ignored.
+# The format is:
+#       TAG = value [value, ...]
+# For lists items can also be appended using:
+#       TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (" ").
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# This tag specifies the encoding used for all characters in the config file
+# that follow. The default is UTF-8 which is also the encoding used for all
+# text before the first occurrence of this tag. Doxygen uses libiconv (or the
+# iconv built into libc) for the transcoding. See
+# http://www.gnu.org/software/libiconv for the list of possible encodings.
+
+DOXYFILE_ENCODING      = UTF-8
+
+# The PROJECT_NAME tag is a single word (or sequence of words) that should
+# identify the project. Note that if you do not use Doxywizard you need
+# to put quotes around the project name if it contains spaces.
+
+PROJECT_NAME           = "pjsua2"
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number.
+# This could be handy for archiving the generated documentation or
+# if some version control system is used.
+
+PROJECT_NUMBER         =
+
+# Using the PROJECT_BRIEF tag one can provide an optional one line description
+# for a project that appears at the top of each page and should give viewer
+# a quick idea about the purpose of the project. Keep the description short.
+
+PROJECT_BRIEF          =
+
+# With the PROJECT_LOGO tag one can specify an logo or icon that is
+# included in the documentation. The maximum height of the logo should not
+# exceed 55 pixels and the maximum width should not exceed 200 pixels.
+# Doxygen will copy the logo to the output directory.
+
+PROJECT_LOGO           =
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
+# base path where the generated documentation will be put.
+# If a relative path is entered, it will be relative to the location
+# where doxygen was started. If left blank the current directory will be used.
+
+OUTPUT_DIRECTORY       =
+
+# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create
+# 4096 sub-directories (in 2 levels) under the output directory of each output
+# format and will distribute the generated files over these directories.
+# Enabling this option can be useful when feeding doxygen a huge amount of
+# source files, where putting all generated files in the same directory would
+# otherwise cause performance problems for the file system.
+
+CREATE_SUBDIRS         = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# The default language is English, other supported languages are:
+# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional,
+# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German,
+# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English
+# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian,
+# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak,
+# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese.
+
+OUTPUT_LANGUAGE        = English
+
+# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
+# include brief member descriptions after the members that are listed in
+# the file and class documentation (similar to JavaDoc).
+# Set to NO to disable this.
+
+BRIEF_MEMBER_DESC      = YES
+
+# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend
+# the brief description of a member or function before the detailed description.
+# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+
+REPEAT_BRIEF           = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator
+# that is used to form the text in various listings. Each string
+# in this list, if found as the leading text of the brief description, will be
+# stripped from the text and the result after processing the whole list, is
+# used as the annotated text. Otherwise, the brief description is used as-is.
+# If left blank, the following values are used ("$name" is automatically
+# replaced with the name of the entity): "The $name class" "The $name widget"
+# "The $name file" "is" "provides" "specifies" "contains"
+# "represents" "a" "an" "the"
+
+ABBREVIATE_BRIEF       =
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# Doxygen will generate a detailed section even if there is only a brief
+# description.
+
+ALWAYS_DETAILED_SEC    = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
+# operators of the base classes will not be shown.
+
+INLINE_INHERITED_MEMB  = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full
+# path before files name in the file list and in the header files. If set
+# to NO the shortest path that makes the file name unique will be used.
+
+FULL_PATH_NAMES        = YES
+
+# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag
+# can be used to strip a user-defined part of the path. Stripping is
+# only done if one of the specified strings matches the left-hand part of
+# the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the
+# path to strip.
+
+STRIP_FROM_PATH        =
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of
+# the path mentioned in the documentation of a class, which tells
+# the reader which header file to include in order to use a class.
+# If left blank only the name of the header file containing the class
+# definition is used. Otherwise one should specify the include paths that
+# are normally passed to the compiler using the -I flag.
+
+STRIP_FROM_INC_PATH    =
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
+# (but less readable) file names. This can be useful if your file system
+# doesn't support long names like on DOS, Mac, or CD-ROM.
+
+SHORT_NAMES            = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen
+# will interpret the first line (until the first dot) of a JavaDoc-style
+# comment as the brief description. If set to NO, the JavaDoc
+# comments will behave just like regular Qt-style comments
+# (thus requiring an explicit @brief command for a brief description.)
+
+JAVADOC_AUTOBRIEF      = YES
+
+# If the QT_AUTOBRIEF tag is set to YES then Doxygen will
+# interpret the first line (until the first dot) of a Qt-style
+# comment as the brief description. If set to NO, the comments
+# will behave just like regular Qt-style comments (thus requiring
+# an explicit \brief command for a brief description.)
+
+QT_AUTOBRIEF           = NO
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen
+# treat a multi-line C++ special comment block (i.e. a block of //! or ///
+# comments) as a brief description. This used to be the default behaviour.
+# The new default is to treat a multi-line C++ comment block as a detailed
+# description. Set this tag to YES if you prefer the old behaviour instead.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
+# member inherits the documentation from any documented member that it
+# re-implements.
+
+INHERIT_DOCS           = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce
+# a new page for each member. If set to NO, the documentation of a member will
+# be part of the file/class/namespace that contains it.
+
+SEPARATE_MEMBER_PAGES  = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab.
+# Doxygen uses this value to replace tabs by spaces in code fragments.
+
+TAB_SIZE               = 8
+
+# This tag can be used to specify a number of aliases that acts
+# as commands in the documentation. An alias has the form "name=value".
+# For example adding "sideeffect=\par Side Effects:\n" will allow you to
+# put the command \sideeffect (or @sideeffect) in the documentation, which
+# will result in a user-defined paragraph with heading "Side Effects:".
+# You can put \n's in the value part of an alias to insert newlines.
+
+ALIASES                =
+
+# This tag can be used to specify a number of word-keyword mappings (TCL only).
+# A mapping has the form "name=value". For example adding
+# "class=itcl::class" will allow you to use the command class in the
+# itcl::class meaning.
+
+TCL_SUBST              =
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C
+# sources only. Doxygen will then generate output that is more tailored for C.
+# For instance, some of the names that are used will be different. The list
+# of all members will be omitted, etc.
+
+OPTIMIZE_OUTPUT_FOR_C  = NO
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java
+# sources only. Doxygen will then generate output that is more tailored for
+# Java. For instance, namespaces will be presented as packages, qualified
+# scopes will look different, etc.
+
+OPTIMIZE_OUTPUT_JAVA   = NO
+
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
+# sources only. Doxygen will then generate output that is more tailored for
+# Fortran.
+
+OPTIMIZE_FOR_FORTRAN   = NO
+
+# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
+# sources. Doxygen will then generate output that is tailored for
+# VHDL.
+
+OPTIMIZE_OUTPUT_VHDL   = NO
+
+# Doxygen selects the parser to use depending on the extension of the files it
+# parses. With this tag you can assign which parser to use for a given extension.
+# Doxygen has a built-in mapping, but you can override or extend it using this
+# tag. The format is ext=language, where ext is a file extension, and language
+# is one of the parsers supported by doxygen: IDL, Java, Javascript, CSharp, C,
+# C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, C++. For instance to make
+# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C
+# (default is Fortran), use: inc=Fortran f=C. Note that for custom extensions
+# you also need to set FILE_PATTERNS otherwise the files are not read by doxygen.
+
+EXTENSION_MAPPING      =
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
+# to include (a tag file for) the STL sources as input, then you should
+# set this tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string); v.s.
+# func(std::string) {}). This also makes the inheritance and collaboration
+# diagrams that involve STL classes more complete and accurate.
+
+BUILTIN_STL_SUPPORT    = NO
+
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# enable parsing support.
+
+CPP_CLI_SUPPORT        = NO
+
+# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only.
+# Doxygen will parse them like normal C++ but will assume all classes use public
+# instead of private inheritance when no explicit protection keyword is present.
+
+SIP_SUPPORT            = NO
+
+# For Microsoft's IDL there are propget and propput attributes to indicate getter
+# and setter methods for a property. Setting this option to YES (the default)
+# will make doxygen replace the get and set methods by a property in the
+# documentation. This will only work if the methods are indeed getting or
+# setting a simple type. If this is not the case, or you want to show the
+# methods anyway, you should set this option to NO.
+
+IDL_PROPERTY_SUPPORT   = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES, then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+
+DISTRIBUTE_GROUP_DOC   = NO
+
+# Set the SUBGROUPING tag to YES (the default) to allow class member groups of
+# the same type (for instance a group of public functions) to be put as a
+# subgroup of that type (e.g. under the Public Functions section). Set it to
+# NO to prevent subgrouping. Alternatively, this can be done per class using
+# the \nosubgrouping command.
+
+SUBGROUPING            = YES
+
+# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and
+# unions are shown inside the group in which they are included (e.g. using
+# @ingroup) instead of on a separate page (for HTML and Man pages) or
+# section (for LaTeX and RTF).
+
+INLINE_GROUPED_CLASSES = NO
+
+# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and
+# unions with only public data fields will be shown inline in the documentation
+# of the scope in which they are defined (i.e. file, namespace, or group
+# documentation), provided this scope is documented. If set to NO (the default),
+# structs, classes, and unions are shown on a separate page (for HTML and Man
+# pages) or section (for LaTeX and RTF).
+
+INLINE_SIMPLE_STRUCTS  = NO
+
+# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum
+# is documented as struct, union, or enum with the name of the typedef. So
+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
+# with name TypeT. When disabled the typedef will appear as a member of a file,
+# namespace, or class. And the struct will be named TypeS. This can typically
+# be useful for C code in case the coding convention dictates that all compound
+# types are typedef'ed and only the typedef is referenced, never the tag name.
+
+TYPEDEF_HIDES_STRUCT   = NO
+
+# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to
+# determine which symbols to keep in memory and which to flush to disk.
+# When the cache is full, less often used symbols will be written to disk.
+# For small to medium size projects (<1000 input files) the default value is
+# probably good enough. For larger projects a too small cache size can cause
+# doxygen to be busy swapping symbols to and from disk most of the time
+# causing a significant performance penalty.
+# If the system has enough physical memory increasing the cache will improve the
+# performance by keeping more symbols in memory. Note that the value works on
+# a logarithmic scale so increasing the size by one will roughly double the
+# memory usage. The cache size is given by this formula:
+# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0,
+# corresponding to a cache size of 2^16 = 65536 symbols.
+
+SYMBOL_CACHE_SIZE      = 0
+
+# Similar to the SYMBOL_CACHE_SIZE the size of the symbol lookup cache can be
+# set using LOOKUP_CACHE_SIZE. This cache is used to resolve symbols given
+# their name and scope. Since this can be an expensive process and often the
+# same symbol appear multiple times in the code, doxygen keeps a cache of
+# pre-resolved symbols. If the cache is too small doxygen will become slower.
+# If the cache is too large, memory is wasted. The cache size is given by this
+# formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range is 0..9, the default is 0,
+# corresponding to a cache size of 2^16 = 65536 symbols.
+
+LOOKUP_CACHE_SIZE      = 0
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
+# documentation are documented, even if no documentation was available.
+# Private class members and static file members will be hidden unless
+# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
+
+EXTRACT_ALL            = NO
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class
+# will be included in the documentation.
+
+EXTRACT_PRIVATE        = NO
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file
+# will be included in the documentation.
+
+EXTRACT_STATIC         = NO
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs)
+# defined locally in source files will be included in the documentation.
+# If set to NO only classes defined in header files are included.
+
+EXTRACT_LOCAL_CLASSES  = YES
+
+# This flag is only useful for Objective-C code. When set to YES local
+# methods, which are defined in the implementation section but not in
+# the interface are included in the documentation.
+# If set to NO (the default) only methods in the interface are included.
+
+EXTRACT_LOCAL_METHODS  = NO
+
+# If this flag is set to YES, the members of anonymous namespaces will be
+# extracted and appear in the documentation as a namespace called
+# 'anonymous_namespace{file}', where file will be replaced with the base
+# name of the file that contains the anonymous namespace. By default
+# anonymous namespaces are hidden.
+
+EXTRACT_ANON_NSPACES   = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
+# undocumented members of documented classes, files or namespaces.
+# If set to NO (the default) these members will be included in the
+# various overviews, but no documentation section is generated.
+# This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_MEMBERS     = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy.
+# If set to NO (the default) these classes will be included in the various
+# overviews. This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_CLASSES     = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all
+# friend (class|struct|union) declarations.
+# If set to NO (the default) these declarations will be included in the
+# documentation.
+
+HIDE_FRIEND_COMPOUNDS  = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any
+# documentation blocks found inside the body of a function.
+# If set to NO (the default) these blocks will be appended to the
+# function's detailed documentation block.
+
+HIDE_IN_BODY_DOCS      = NO
+
+# The INTERNAL_DOCS tag determines if documentation
+# that is typed after a \internal command is included. If the tag is set
+# to NO (the default) then the documentation will be excluded.
+# Set it to YES to include the internal documentation.
+
+INTERNAL_DOCS          = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate
+# file names in lower-case letters. If set to YES upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# and Mac users are advised to set this option to NO.
+
+CASE_SENSE_NAMES       = YES
+
+# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen
+# will show members with their full class and namespace scopes in the
+# documentation. If set to YES the scope will be hidden.
+
+HIDE_SCOPE_NAMES       = NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen
+# will put a list of the files that are included by a file in the documentation
+# of that file.
+
+SHOW_INCLUDE_FILES     = YES
+
+# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen
+# will list include files with double quotes in the documentation
+# rather than with sharp brackets.
+
+FORCE_LOCAL_INCLUDES   = NO
+
+# If the INLINE_INFO tag is set to YES (the default) then a tag [inline]
+# is inserted in the documentation for inline members.
+
+INLINE_INFO            = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen
+# will sort the (detailed) documentation of file and class members
+# alphabetically by member name. If set to NO the members will appear in
+# declaration order.
+
+SORT_MEMBER_DOCS       = YES
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the
+# brief documentation of file, namespace and class members alphabetically
+# by member name. If set to NO (the default) the members will appear in
+# declaration order.
+
+SORT_BRIEF_DOCS        = NO
+
+# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen
+# will sort the (brief and detailed) documentation of class members so that
+# constructors and destructors are listed first. If set to NO (the default)
+# the constructors will appear in the respective orders defined by
+# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS.
+# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO
+# and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO.
+
+SORT_MEMBERS_CTORS_1ST = NO
+
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the
+# hierarchy of group names into alphabetical order. If set to NO (the default)
+# the group names will appear in their defined order.
+
+SORT_GROUP_NAMES       = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be
+# sorted by fully-qualified names, including namespaces. If set to
+# NO (the default), the class list will be sorted only by class name,
+# not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the
+# alphabetical list.
+
+SORT_BY_SCOPE_NAME     = NO
+
+# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to
+# do proper type resolution of all parameters of a function it will reject a
+# match between the prototype and the implementation of a member function even
+# if there is only one candidate or it is obvious which candidate to choose
+# by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen
+# will still accept a match between prototype and implementation in such cases.
+
+STRICT_PROTO_MATCHING  = NO
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or
+# disable (NO) the todo list. This list is created by putting \todo
+# commands in the documentation.
+
+GENERATE_TODOLIST      = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or
+# disable (NO) the test list. This list is created by putting \test
+# commands in the documentation.
+
+GENERATE_TESTLIST      = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or
+# disable (NO) the bug list. This list is created by putting \bug
+# commands in the documentation.
+
+GENERATE_BUGLIST       = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or
+# disable (NO) the deprecated list. This list is created by putting
+# \deprecated commands in the documentation.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional
+# documentation sections, marked by \if sectionname ... \endif.
+
+ENABLED_SECTIONS       =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines
+# the initial value of a variable or macro consists of for it to appear in
+# the documentation. If the initializer consists of more lines than specified
+# here it will be hidden. Use a value of 0 to hide initializers completely.
+# The appearance of the initializer of individual variables and macros in the
+# documentation can be controlled using \showinitializer or \hideinitializer
+# command in the documentation regardless of this setting.
+
+MAX_INITIALIZER_LINES  = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated
+# at the bottom of the documentation of classes and structs. If set to YES the
+# list will mention the files that were used to generate the documentation.
+
+SHOW_USED_FILES        = YES
+
+# If the sources in your project are distributed over multiple directories
+# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy
+# in the documentation. The default is NO.
+
+SHOW_DIRECTORIES       = NO
+
+# Set the SHOW_FILES tag to NO to disable the generation of the Files page.
+# This will remove the Files entry from the Quick Index and from the
+# Folder Tree View (if specified). The default is YES.
+
+SHOW_FILES             = YES
+
+# Set the SHOW_NAMESPACES tag to NO to disable the generation of the
+# Namespaces page.
+# This will remove the Namespaces entry from the Quick Index
+# and from the Folder Tree View (if specified). The default is YES.
+
+SHOW_NAMESPACES        = YES
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from
+# the version control system). Doxygen will invoke the program by executing (via
+# popen()) the command <command> <input-file>, where <command> is the value of
+# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file
+# provided by doxygen. Whatever the program writes to standard output
+# is used as the file version. See the manual for examples.
+
+FILE_VERSION_FILTER    =
+
+# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
+# by doxygen. The layout file controls the global structure of the generated
+# output files in an output format independent way. The create the layout file
+# that represents doxygen's defaults, run doxygen with the -l option.
+# You can optionally specify a file name after the option, if omitted
+# DoxygenLayout.xml will be used as the name of the layout file.
+
+LAYOUT_FILE            =
+
+# The CITE_BIB_FILES tag can be used to specify one or more bib files
+# containing the references data. This must be a list of .bib files. The
+# .bib extension is automatically appended if omitted. Using this command
+# requires the bibtex tool to be installed. See also
+# http://en.wikipedia.org/wiki/BibTeX for more info. For LaTeX the style
+# of the bibliography can be controlled using LATEX_BIB_STYLE. To use this
+# feature you need bibtex and perl available in the search path.
+
+CITE_BIB_FILES         =
+
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated
+# by doxygen. Possible values are YES and NO. If left blank NO is used.
+
+QUIET                  = YES
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated by doxygen. Possible values are YES and NO. If left blank
+# NO is used.
+
+WARNINGS               = YES
+
+# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings
+# for undocumented members. If EXTRACT_ALL is set to YES then this flag will
+# automatically be disabled.
+
+WARN_IF_UNDOCUMENTED   = YES
+
+# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some
+# parameters in a documented function, or documenting parameters that
+# don't exist or using markup commands wrongly.
+
+WARN_IF_DOC_ERROR      = YES
+
+# The WARN_NO_PARAMDOC option can be enabled to get warnings for
+# functions that are documented, but have no documentation for their parameters
+# or return value. If set to NO (the default) doxygen will only warn about
+# wrong or incomplete parameter documentation, but not about the absence of
+# documentation.
+
+WARN_NO_PARAMDOC       = NO
+
+# The WARN_FORMAT tag determines the format of the warning messages that
+# doxygen can produce. The string should contain the $file, $line, and $text
+# tags, which will be replaced by the file and line number from which the
+# warning originated and the warning text. Optionally the format may contain
+# $version, which will be replaced by the version of the file (if it could
+# be obtained via FILE_VERSION_FILTER)
+
+WARN_FORMAT            = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning
+# and error messages should be written. If left blank the output is written
+# to stderr.
+
+WARN_LOGFILE           =
+
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag can be used to specify the files and/or directories that contain
+# documented source files. You may enter file names like "myfile.cpp" or
+# directories like "/usr/src/myproject". Separate the files or directories
+# with spaces.
+
+INPUT                  = ../../pjsip/include/pjsua2/media.hpp
+
+# This tag can be used to specify the character encoding of the source files
+# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
+# also the default input encoding. Doxygen uses libiconv (or the iconv built
+# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for
+# the list of possible encodings.
+
+INPUT_ENCODING         = UTF-8
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank the following patterns are tested:
+# *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh
+# *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py
+# *.f90 *.f *.for *.vhd *.vhdl
+
+FILE_PATTERNS          = *.hpp
+
+# The RECURSIVE tag can be used to turn specify whether or not subdirectories
+# should be searched for input files as well. Possible values are YES and NO.
+# If left blank NO is used.
+
+RECURSIVE              = NO
+
+# The EXCLUDE tag can be used to specify files and/or directories that should be
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+# Note that relative paths are relative to the directory from which doxygen is
+# run.
+
+EXCLUDE                =
+
+# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
+# directories that are symbolic links (a Unix file system feature) are excluded
+# from the input.
+
+EXCLUDE_SYMLINKS       = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories. Note that the wildcards are matched
+# against the file with absolute path, so to exclude all test directories
+# for example use the pattern */test/*
+
+EXCLUDE_PATTERNS       =
+
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
+# (namespaces, classes, functions, etc.) that should be excluded from the
+# output. The symbol name can be a fully qualified name, a word, or if the
+# wildcard * is used, a substring. Examples: ANamespace, AClass,
+# AClass::ANamespace, ANamespace::*Test
+
+EXCLUDE_SYMBOLS        =
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or
+# directories that contain example code fragments that are included (see
+# the \include command).
+
+EXAMPLE_PATH           =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank all files are included.
+
+EXAMPLE_PATTERNS       =
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude
+# commands irrespective of the value of the RECURSIVE tag.
+# Possible values are YES and NO. If left blank NO is used.
+
+EXAMPLE_RECURSIVE      = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or
+# directories that contain image that are included in the documentation (see
+# the \image command).
+
+IMAGE_PATH             =
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command <filter> <input-file>, where <filter>
+# is the value of the INPUT_FILTER tag, and <input-file> is the name of an
+# input file. Doxygen will then use the output that the filter program writes
+# to standard output.
+# If FILTER_PATTERNS is specified, this tag will be
+# ignored.
+
+INPUT_FILTER           =
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis.
+# Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match.
+# The filters are a list of the form:
+# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further
+# info on how filters are used. If FILTER_PATTERNS is empty or if
+# non of the patterns match the file name, INPUT_FILTER is applied.
+
+FILTER_PATTERNS        =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER) will be used to filter the input files when producing source
+# files to browse (i.e. when SOURCE_BROWSER is set to YES).
+
+FILTER_SOURCE_FILES    = NO
+
+# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file
+# pattern. A pattern will override the setting for FILTER_PATTERN (if any)
+# and it is also possible to disable source filtering for a specific pattern
+# using *.ext= (so without naming a filter). This option only has effect when
+# FILTER_SOURCE_FILES is enabled.
+
+FILTER_SOURCE_PATTERNS =
+
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will
+# be generated. Documented entities will be cross-referenced with these sources.
+# Note: To get rid of all source code in the generated output, make sure also
+# VERBATIM_HEADERS is set to NO.
+
+SOURCE_BROWSER         = NO
+
+# Setting the INLINE_SOURCES tag to YES will include the body
+# of functions and classes directly in the documentation.
+
+INLINE_SOURCES         = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct
+# doxygen to hide any special comment blocks from generated source code
+# fragments. Normal C and C++ comments will always remain visible.
+
+STRIP_CODE_COMMENTS    = YES
+
+# If the REFERENCED_BY_RELATION tag is set to YES
+# then for each documented function all documented
+# functions referencing it will be listed.
+
+REFERENCED_BY_RELATION = NO
+
+# If the REFERENCES_RELATION tag is set to YES
+# then for each documented function all documented entities
+# called/used by that function will be listed.
+
+REFERENCES_RELATION    = NO
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES (the default)
+# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from
+# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will
+# link to the source code.
+# Otherwise they will link to the documentation.
+
+REFERENCES_LINK_SOURCE = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code
+# will point to the HTML generated by the htags(1) tool instead of doxygen
+# built-in source browser. The htags tool is part of GNU's global source
+# tagging system (see http://www.gnu.org/software/global/global.html). You
+# will need version 4.8.6 or higher.
+
+USE_HTAGS              = NO
+
+# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen
+# will generate a verbatim copy of the header file for each class for
+# which an include is specified. Set to NO to disable this.
+
+VERBATIM_HEADERS       = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index
+# of all compounds will be generated. Enable this if the project
+# contains a lot of classes, structs, unions or interfaces.
+
+ALPHABETICAL_INDEX     = YES
+
+# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then
+# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns
+# in which this list will be split (can be a number in the range [1..20])
+
+COLS_IN_ALPHA_INDEX    = 5
+
+# In case all classes in a project start with a common prefix, all
+# classes will be put under the same header in the alphabetical index.
+# The IGNORE_PREFIX tag can be used to specify one or more prefixes that
+# should be ignored while generating the index headers.
+
+IGNORE_PREFIX          =
+
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES (the default) Doxygen will
+# generate HTML output.
+
+GENERATE_HTML          = NO
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `html' will be used as the default path.
+
+HTML_OUTPUT            = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for
+# each generated HTML page (for example: .htm,.php,.asp). If it is left blank
+# doxygen will generate files with .html extension.
+
+HTML_FILE_EXTENSION    = .html
+
+# The HTML_HEADER tag can be used to specify a personal HTML header for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard header. Note that when using a custom header you are responsible
+#  for the proper inclusion of any scripts and style sheets that doxygen
+# needs, which is dependent on the configuration options used.
+# It is advised to generate a default header using "doxygen -w html
+# header.html footer.html stylesheet.css YourConfigFile" and then modify
+# that header. Note that the header is subject to change so you typically
+# have to redo this when upgrading to a newer version of doxygen or when
+# changing the value of configuration settings such as GENERATE_TREEVIEW!
+
+HTML_HEADER            =
+
+# The HTML_FOOTER tag can be used to specify a personal HTML footer for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard footer.
+
+HTML_FOOTER            =
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading
+# style sheet that is used by each HTML page. It can be used to
+# fine-tune the look of the HTML output. If the tag is left blank doxygen
+# will generate a default style sheet. Note that doxygen will try to copy
+# the style sheet file to the HTML output directory, so don't put your own
+# style sheet in the HTML output directory as well, or it will be erased!
+
+HTML_STYLESHEET        =
+
+# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the HTML output directory. Note
+# that these files will be copied to the base HTML output directory. Use the
+# $relpath$ marker in the HTML_HEADER and/or HTML_FOOTER files to load these
+# files. In the HTML_STYLESHEET file, use the file name only. Also note that
+# the files will be copied as-is; there are no commands or markers available.
+
+HTML_EXTRA_FILES       =
+
+# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output.
+# Doxygen will adjust the colors in the style sheet and background images
+# according to this color. Hue is specified as an angle on a colorwheel,
+# see http://en.wikipedia.org/wiki/Hue for more information.
+# For instance the value 0 represents red, 60 is yellow, 120 is green,
+# 180 is cyan, 240 is blue, 300 purple, and 360 is red again.
+# The allowed range is 0 to 359.
+
+HTML_COLORSTYLE_HUE    = 220
+
+# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of
+# the colors in the HTML output. For a value of 0 the output will use
+# grayscales only. A value of 255 will produce the most vivid colors.
+
+HTML_COLORSTYLE_SAT    = 100
+
+# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to
+# the luminance component of the colors in the HTML output. Values below
+# 100 gradually make the output lighter, whereas values above 100 make
+# the output darker. The value divided by 100 is the actual gamma applied,
+# so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2,
+# and 100 does not change the gamma.
+
+HTML_COLORSTYLE_GAMMA  = 80
+
+# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
+# page will contain the date and time when the page was generated. Setting
+# this to NO can help when comparing the output of multiple runs.
+
+HTML_TIMESTAMP         = YES
+
+# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes,
+# files or namespaces will be aligned in HTML using tables. If set to
+# NO a bullet list will be used.
+
+HTML_ALIGN_MEMBERS     = YES
+
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
+# documentation will contain sections that can be hidden and shown after the
+# page has loaded. For this to work a browser that supports
+# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox
+# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari).
+
+HTML_DYNAMIC_SECTIONS  = NO
+
+# If the GENERATE_DOCSET tag is set to YES, additional index files
+# will be generated that can be used as input for Apple's Xcode 3
+# integrated development environment, introduced with OSX 10.5 (Leopard).
+# To create a documentation set, doxygen will generate a Makefile in the
+# HTML output directory. Running make will produce the docset in that
+# directory and running "make install" will install the docset in
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find
+# it at startup.
+# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html
+# for more information.
+
+GENERATE_DOCSET        = NO
+
+# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the
+# feed. A documentation feed provides an umbrella under which multiple
+# documentation sets from a single provider (such as a company or product suite)
+# can be grouped.
+
+DOCSET_FEEDNAME        = "Doxygen generated docs"
+
+# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that
+# should uniquely identify the documentation set bundle. This should be a
+# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen
+# will append .docset to the name.
+
+DOCSET_BUNDLE_ID       = org.doxygen.Project
+
+# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely identify
+# the documentation publisher. This should be a reverse domain-name style
+# string, e.g. com.mycompany.MyDocSet.documentation.
+
+DOCSET_PUBLISHER_ID    = org.doxygen.Publisher
+
+# The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher.
+
+DOCSET_PUBLISHER_NAME  = Publisher
+
+# If the GENERATE_HTMLHELP tag is set to YES, additional index files
+# will be generated that can be used as input for tools like the
+# Microsoft HTML help workshop to generate a compiled HTML help file (.chm)
+# of the generated HTML documentation.
+
+GENERATE_HTMLHELP      = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can
+# be used to specify the file name of the resulting .chm file. You
+# can add a path in front of the file if the result should not be
+# written to the html output directory.
+
+CHM_FILE               =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can
+# be used to specify the location (absolute path including file name) of
+# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run
+# the HTML help compiler on the generated index.hhp.
+
+HHC_LOCATION           =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag
+# controls if a separate .chi index file is generated (YES) or that
+# it should be included in the master .chm file (NO).
+
+GENERATE_CHI           = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING
+# is used to encode HtmlHelp index (hhk), content (hhc) and project file
+# content.
+
+CHM_INDEX_ENCODING     =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag
+# controls whether a binary table of contents is generated (YES) or a
+# normal table of contents (NO) in the .chm file.
+
+BINARY_TOC             = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members
+# to the contents of the HTML help documentation and to the tree view.
+
+TOC_EXPAND             = NO
+
+# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
+# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated
+# that can be used as input for Qt's qhelpgenerator to generate a
+# Qt Compressed Help (.qch) of the generated HTML documentation.
+
+GENERATE_QHP           = NO
+
+# If the QHG_LOCATION tag is specified, the QCH_FILE tag can
+# be used to specify the file name of the resulting .qch file.
+# The path specified is relative to the HTML output folder.
+
+QCH_FILE               =
+
+# The QHP_NAMESPACE tag specifies the namespace to use when generating
+# Qt Help Project output. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#namespace
+
+QHP_NAMESPACE          = org.doxygen.Project
+
+# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating
+# Qt Help Project output. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#virtual-folders
+
+QHP_VIRTUAL_FOLDER     = doc
+
+# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to
+# add. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#custom-filters
+
+QHP_CUST_FILTER_NAME   =
+
+# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the
+# custom filter to add. For more information please see
+# <a href="http://doc.trolltech.com/qthelpproject.html#custom-filters">
+# Qt Help Project / Custom Filters</a>.
+
+QHP_CUST_FILTER_ATTRS  =
+
+# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
+# project's
+# filter section matches.
+# <a href="http://doc.trolltech.com/qthelpproject.html#filter-attributes">
+# Qt Help Project / Filter Attributes</a>.
+
+QHP_SECT_FILTER_ATTRS  =
+
+# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can
+# be used to specify the location of Qt's qhelpgenerator.
+# If non-empty doxygen will try to run qhelpgenerator on the generated
+# .qhp file.
+
+QHG_LOCATION           =
+
+# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files
+#  will be generated, which together with the HTML files, form an Eclipse help
+# plugin. To install this plugin and make it available under the help contents
+# menu in Eclipse, the contents of the directory containing the HTML and XML
+# files needs to be copied into the plugins directory of eclipse. The name of
+# the directory within the plugins directory should be the same as
+# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before
+# the help appears.
+
+GENERATE_ECLIPSEHELP   = NO
+
+# A unique identifier for the eclipse help plugin. When installing the plugin
+# the directory name containing the HTML and XML files should also have
+# this name.
+
+ECLIPSE_DOC_ID         = org.doxygen.Project
+
+# The DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs)
+# at top of each HTML page. The value NO (the default) enables the index and
+# the value YES disables it. Since the tabs have the same information as the
+# navigation tree you can set this option to NO if you already set
+# GENERATE_TREEVIEW to YES.
+
+DISABLE_INDEX          = NO
+
+# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
+# structure should be generated to display hierarchical information.
+# If the tag value is set to YES, a side panel will be generated
+# containing a tree-like index structure (just like the one that
+# is generated for HTML Help). For this to work a browser that supports
+# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser).
+# Windows users are probably better off using the HTML help feature.
+# Since the tree basically has the same information as the tab index you
+# could consider to set DISABLE_INDEX to NO when enabling this option.
+
+GENERATE_TREEVIEW      = NO
+
+# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values
+# (range [0,1..20]) that doxygen will group on one line in the generated HTML
+# documentation. Note that a value of 0 will completely suppress the enum
+# values from appearing in the overview section.
+
+ENUM_VALUES_PER_LINE   = 4
+
+# By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories,
+# and Class Hierarchy pages using a tree view instead of an ordered list.
+
+USE_INLINE_TREES       = NO
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be
+# used to set the initial width (in pixels) of the frame in which the tree
+# is shown.
+
+TREEVIEW_WIDTH         = 250
+
+# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open
+# links to external symbols imported via tag files in a separate window.
+
+EXT_LINKS_IN_WINDOW    = NO
+
+# Use this tag to change the font size of Latex formulas included
+# as images in the HTML documentation. The default is 10. Note that
+# when you change the font size after a successful doxygen run you need
+# to manually remove any form_*.png images from the HTML output directory
+# to force them to be regenerated.
+
+FORMULA_FONTSIZE       = 10
+
+# Use the FORMULA_TRANPARENT tag to determine whether or not the images
+# generated for formulas are transparent PNGs. Transparent PNGs are
+# not supported properly for IE 6.0, but are supported on all modern browsers.
+# Note that when changing this option you need to delete any form_*.png files
+# in the HTML output before the changes have effect.
+
+FORMULA_TRANSPARENT    = YES
+
+# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax
+# (see http://www.mathjax.org) which uses client side Javascript for the
+# rendering instead of using prerendered bitmaps. Use this if you do not
+# have LaTeX installed or if you want to formulas look prettier in the HTML
+# output. When enabled you also need to install MathJax separately and
+# configure the path to it using the MATHJAX_RELPATH option.
+
+USE_MATHJAX            = NO
+
+# When MathJax is enabled you need to specify the location relative to the
+# HTML output directory using the MATHJAX_RELPATH option. The destination
+# directory should contain the MathJax.js script. For instance, if the mathjax
+# directory is located at the same level as the HTML output directory, then
+# MATHJAX_RELPATH should be ../mathjax. The default value points to the
+# mathjax.org site, so you can quickly see the result without installing
+# MathJax, but it is strongly recommended to install a local copy of MathJax
+# before deployment.
+
+MATHJAX_RELPATH        = http://www.mathjax.org/mathjax
+
+# The MATHJAX_EXTENSIONS tag can be used to specify one or MathJax extension
+# names that should be enabled during MathJax rendering.
+
+MATHJAX_EXTENSIONS     =
+
+# When the SEARCHENGINE tag is enabled doxygen will generate a search box
+# for the HTML output. The underlying search engine uses javascript
+# and DHTML and should work on any modern browser. Note that when using
+# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets
+# (GENERATE_DOCSET) there is already a search function so this one should
+# typically be disabled. For large projects the javascript based search engine
+# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution.
+
+SEARCHENGINE           = YES
+
+# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
+# implemented using a PHP enabled web server instead of at the web client
+# using Javascript. Doxygen will generate the search PHP script and index
+# file to put on the web server. The advantage of the server
+# based approach is that it scales better to large projects and allows
+# full text search. The disadvantages are that it is more difficult to setup
+# and does not have live searching capabilities.
+
+SERVER_BASED_SEARCH    = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
+# generate Latex output.
+
+GENERATE_LATEX         = NO
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `latex' will be used as the default path.
+
+LATEX_OUTPUT           = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked. If left blank `latex' will be used as the default command name.
+# Note that when enabling USE_PDFLATEX this option is only used for
+# generating bitmaps for formulas in the HTML output, but not in the
+# Makefile that is written to the output directory.
+
+LATEX_CMD_NAME         = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to
+# generate index for LaTeX. If left blank `makeindex' will be used as the
+# default command name.
+
+MAKEINDEX_CMD_NAME     = makeindex
+
+# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact
+# LaTeX documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_LATEX          = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used
+# by the printer. Possible values are: a4, letter, legal and
+# executive. If left blank a4wide will be used.
+
+PAPER_TYPE             = a4
+
+# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
+# packages that should be included in the LaTeX output.
+
+EXTRA_PACKAGES         =
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for
+# the generated latex document. The header should contain everything until
+# the first chapter. If it is left blank doxygen will generate a
+# standard header. Notice: only use this tag if you know what you are doing!
+
+LATEX_HEADER           =
+
+# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for
+# the generated latex document. The footer should contain everything after
+# the last chapter. If it is left blank doxygen will generate a
+# standard footer. Notice: only use this tag if you know what you are doing!
+
+LATEX_FOOTER           =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated
+# is prepared for conversion to pdf (using ps2pdf). The pdf file will
+# contain links (just like the HTML output) instead of page references
+# This makes the output suitable for online browsing using a pdf viewer.
+
+PDF_HYPERLINKS         = YES
+
+# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of
+# plain latex in the generated Makefile. Set this option to YES to get a
+# higher quality PDF documentation.
+
+USE_PDFLATEX           = YES
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode.
+# command to the generated LaTeX files. This will instruct LaTeX to keep
+# running if errors occur, instead of asking the user for help.
+# This option is also used when generating formulas in HTML.
+
+LATEX_BATCHMODE        = NO
+
+# If LATEX_HIDE_INDICES is set to YES then doxygen will not
+# include the index chapters (such as File Index, Compound Index, etc.)
+# in the output.
+
+LATEX_HIDE_INDICES     = NO
+
+# If LATEX_SOURCE_CODE is set to YES then doxygen will include
+# source code with syntax highlighting in the LaTeX output.
+# Note that which sources are shown also depends on other settings
+# such as SOURCE_BROWSER.
+
+LATEX_SOURCE_CODE      = NO
+
+# The LATEX_BIB_STYLE tag can be used to specify the style to use for the
+# bibliography, e.g. plainnat, or ieeetr. The default style is "plain". See
+# http://en.wikipedia.org/wiki/BibTeX for more info.
+
+LATEX_BIB_STYLE        = plain
+
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output
+# The RTF output is optimized for Word 97 and may not look very pretty with
+# other RTF readers or editors.
+
+GENERATE_RTF           = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `rtf' will be used as the default path.
+
+RTF_OUTPUT             = rtf
+
+# If the COMPACT_RTF tag is set to YES Doxygen generates more compact
+# RTF documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_RTF            = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated
+# will contain hyperlink fields. The RTF file will
+# contain links (just like the HTML output) instead of page references.
+# This makes the output suitable for online browsing using WORD or other
+# programs which support those fields.
+# Note: wordpad (write) and others do not support links.
+
+RTF_HYPERLINKS         = NO
+
+# Load style sheet definitions from file. Syntax is similar to doxygen's
+# config file, i.e. a series of assignments. You only have to provide
+# replacements, missing definitions are set to their default value.
+
+RTF_STYLESHEET_FILE    =
+
+# Set optional variables used in the generation of an rtf document.
+# Syntax is similar to doxygen's config file.
+
+RTF_EXTENSIONS_FILE    =
+
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES (the default) Doxygen will
+# generate man pages
+
+GENERATE_MAN           = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `man' will be used as the default path.
+
+MAN_OUTPUT             = man
+
+# The MAN_EXTENSION tag determines the extension that is added to
+# the generated man pages (default is the subroutine's section .3)
+
+MAN_EXTENSION          = .3
+
+# If the MAN_LINKS tag is set to YES and Doxygen generates man output,
+# then it will generate one additional man file for each entity
+# documented in the real man page(s). These additional files
+# only source the real man page, but without them the man command
+# would be unable to find the correct page. The default is NO.
+
+MAN_LINKS              = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES Doxygen will
+# generate an XML file that captures the structure of
+# the code including all documentation.
+
+GENERATE_XML           = YES
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `xml' will be used as the default path.
+
+XML_OUTPUT             = xml
+
+# The XML_SCHEMA tag can be used to specify an XML schema,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_SCHEMA             =
+
+# The XML_DTD tag can be used to specify an XML DTD,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_DTD                =
+
+# If the XML_PROGRAMLISTING tag is set to YES Doxygen will
+# dump the program listings (including syntax highlighting
+# and cross-referencing information) to the XML output. Note that
+# enabling this will significantly increase the size of the XML output.
+
+XML_PROGRAMLISTING     = YES
+
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will
+# generate an AutoGen Definitions (see autogen.sf.net) file
+# that captures the structure of the code including all
+# documentation. Note that this feature is still experimental
+# and incomplete at the moment.
+
+GENERATE_AUTOGEN_DEF   = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES Doxygen will
+# generate a Perl module file that captures the structure of
+# the code including all documentation. Note that this
+# feature is still experimental and incomplete at the
+# moment.
+
+GENERATE_PERLMOD       = NO
+
+# If the PERLMOD_LATEX tag is set to YES Doxygen will generate
+# the necessary Makefile rules, Perl scripts and LaTeX code to be able
+# to generate PDF and DVI output from the Perl module output.
+
+PERLMOD_LATEX          = NO
+
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be
+# nicely formatted so it can be parsed by a human reader.
+# This is useful
+# if you want to understand what is going on.
+# On the other hand, if this
+# tag is set to NO the size of the Perl module output will be much smaller
+# and Perl will parse it just the same.
+
+PERLMOD_PRETTY         = YES
+
+# The names of the make variables in the generated doxyrules.make file
+# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX.
+# This is useful so different doxyrules.make files included by the same
+# Makefile don't overwrite each other's variables.
+
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
+# evaluate all C-preprocessor directives found in the sources and include
+# files.
+
+ENABLE_PREPROCESSING   = YES
+
+# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro
+# names in the source code. If set to NO (the default) only conditional
+# compilation will be performed. Macro expansion can be done in a controlled
+# way by setting EXPAND_ONLY_PREDEF to YES.
+
+MACRO_EXPANSION        = NO
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES
+# then the macro expansion is limited to the macros specified with the
+# PREDEFINED and EXPAND_AS_DEFINED tags.
+
+EXPAND_ONLY_PREDEF     = NO
+
+# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
+# pointed to by INCLUDE_PATH will be searched when a #include is found.
+
+SEARCH_INCLUDES        = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by
+# the preprocessor.
+
+INCLUDE_PATH           =
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will
+# be used.
+
+INCLUDE_FILE_PATTERNS  =
+
+# The PREDEFINED tag can be used to specify one or more macro names that
+# are defined before the preprocessor is started (similar to the -D option of
+# gcc). The argument of the tag is a list of macros of the form: name
+# or name=definition (no spaces). If the definition and the = are
+# omitted =1 is assumed. To prevent a macro definition from being
+# undefined via #undef or recursively expanded use the := operator
+# instead of the = operator.
+
+PREDEFINED             = DOXYGEN
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
+# this tag can be used to specify a list of macro names that should be expanded.
+# The macro definition that is found in the sources will be used.
+# Use the PREDEFINED tag if you want to use a different macro definition that
+# overrules the definition found in the source code.
+
+EXPAND_AS_DEFINED      =
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
+# doxygen's preprocessor will remove all references to function-like macros
+# that are alone on a line, have an all uppercase name, and do not end with a
+# semicolon, because these will confuse the parser if not removed.
+
+SKIP_FUNCTION_MACROS   = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES option can be used to specify one or more tagfiles.
+# Optionally an initial location of the external documentation
+# can be added for each tagfile. The format of a tag file without
+# this location is as follows:
+#
+# TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+#
+# TAGFILES = file1=loc1 "file2 = loc2" ...
+# where "loc1" and "loc2" can be relative or absolute paths or
+# URLs. If a location is present for each tag, the installdox tool
+# does not have to be run to correct the links.
+# Note that each tag file must have a unique name
+# (where the name does NOT include the path)
+# If a tag file is not located in the directory in which doxygen
+# is run, you must also specify the path to the tagfile here.
+
+TAGFILES               =
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create
+# a tag file that is based on the input files it reads.
+
+GENERATE_TAGFILE       =
+
+# If the ALLEXTERNALS tag is set to YES all external classes will be listed
+# in the class index. If set to NO only the inherited external classes
+# will be listed.
+
+ALLEXTERNALS           = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed
+# in the modules index. If set to NO, only the current project's groups will
+# be listed.
+
+EXTERNAL_GROUPS        = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of `which perl').
+
+PERL_PATH              = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will
+# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base
+# or super classes. Setting the tag to NO turns the diagrams off. Note that
+# this option also works with HAVE_DOT disabled, but it is recommended to
+# install and use dot, since it yields more powerful graphs.
+
+CLASS_DIAGRAMS         = YES
+
+# You can define message sequence charts within doxygen comments using the \msc
+# command. Doxygen will then run the mscgen tool (see
+# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the
+# documentation. The MSCGEN_PATH tag allows you to specify the directory where
+# the mscgen tool resides. If left empty the tool is assumed to be found in the
+# default search path.
+
+MSCGEN_PATH            =
+
+# If set to YES, the inheritance and collaboration graphs will hide
+# inheritance and usage relations if the target is undocumented
+# or is not a class.
+
+HIDE_UNDOC_RELATIONS   = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz, a graph visualization
+# toolkit from AT&T and Lucent Bell Labs. The other options in this section
+# have no effect if this option is set to NO (the default)
+
+HAVE_DOT               = NO
+
+# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is
+# allowed to run in parallel. When set to 0 (the default) doxygen will
+# base this on the number of processors available in the system. You can set it
+# explicitly to a value larger than 0 to get control over the balance
+# between CPU load and processing speed.
+
+DOT_NUM_THREADS        = 0
+
+# By default doxygen will use the Helvetica font for all dot files that
+# doxygen generates. When you want a differently looking font you can specify
+# the font name using DOT_FONTNAME. You need to make sure dot is able to find
+# the font, which can be done by putting it in a standard location or by setting
+# the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the
+# directory containing the font.
+
+DOT_FONTNAME           = Helvetica
+
+# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs.
+# The default size is 10pt.
+
+DOT_FONTSIZE           = 10
+
+# By default doxygen will tell dot to use the Helvetica font.
+# If you specify a different font using DOT_FONTNAME you can use DOT_FONTPATH to
+# set the path where dot can find it.
+
+DOT_FONTPATH           =
+
+# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect inheritance relations. Setting this tag to YES will force the
+# CLASS_DIAGRAMS tag to NO.
+
+CLASS_GRAPH            = YES
+
+# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect implementation dependencies (inheritance, containment, and
+# class references variables) of the class with other documented classes.
+
+COLLABORATION_GRAPH    = YES
+
+# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for groups, showing the direct groups dependencies
+
+GROUP_GRAPHS           = YES
+
+# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
+# Language.
+
+UML_LOOK               = NO
+
+# If set to YES, the inheritance and collaboration graphs will show the
+# relations between templates and their instances.
+
+TEMPLATE_RELATIONS     = NO
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT
+# tags are set to YES then doxygen will generate a graph for each documented
+# file showing the direct and indirect include dependencies of the file with
+# other documented files.
+
+INCLUDE_GRAPH          = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and
+# HAVE_DOT tags are set to YES then doxygen will generate a graph for each
+# documented header file showing the documented files that directly or
+# indirectly include this file.
+
+INCLUDED_BY_GRAPH      = YES
+
+# If the CALL_GRAPH and HAVE_DOT options are set to YES then
+# doxygen will generate a call dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable call graphs
+# for selected functions only using the \callgraph command.
+
+CALL_GRAPH             = NO
+
+# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then
+# doxygen will generate a caller dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable caller
+# graphs for selected functions only using the \callergraph command.
+
+CALLER_GRAPH           = NO
+
+# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
+# will generate a graphical hierarchy of all classes instead of a textual one.
+
+GRAPHICAL_HIERARCHY    = YES
+
+# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES
+# then doxygen will show the dependencies a directory has on other directories
+# in a graphical way. The dependency relations are determined by the #include
+# relations between the files in the directories.
+
+DIRECTORY_GRAPH        = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot. Possible values are svg, png, jpg, or gif.
+# If left blank png will be used. If you choose svg you need to set
+# HTML_FILE_EXTENSION to xhtml in order to make the SVG files
+# visible in IE 9+ (other browsers do not have this requirement).
+
+DOT_IMAGE_FORMAT       = png
+
+# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to
+# enable generation of interactive SVG images that allow zooming and panning.
+# Note that this requires a modern browser other than Internet Explorer.
+# Tested and working are Firefox, Chrome, Safari, and Opera. For IE 9+ you
+# need to set HTML_FILE_EXTENSION to xhtml in order to make the SVG files
+# visible. Older versions of IE do not have SVG support.
+
+INTERACTIVE_SVG        = NO
+
+# The tag DOT_PATH can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found in the path.
+
+DOT_PATH               =
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the
+# \dotfile command).
+
+DOTFILE_DIRS           =
+
+# The MSCFILE_DIRS tag can be used to specify one or more directories that
+# contain msc files that are included in the documentation (see the
+# \mscfile command).
+
+MSCFILE_DIRS           =
+
+# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of
+# nodes that will be shown in the graph. If the number of nodes in a graph
+# becomes larger than this value, doxygen will truncate the graph, which is
+# visualized by representing a node as a red box. Note that doxygen if the
+# number of direct children of the root node in a graph is already larger than
+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note
+# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+
+DOT_GRAPH_MAX_NODES    = 50
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the
+# graphs generated by dot. A depth value of 3 means that only nodes reachable
+# from the root by following a path via at most 3 edges will be shown. Nodes
+# that lay further from the root node will be omitted. Note that setting this
+# option to 1 or 2 may greatly reduce the computation time needed for large
+# code bases. Also note that the size of a graph can be further restricted by
+# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
+
+MAX_DOT_GRAPH_DEPTH    = 0
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is disabled by default, because dot on Windows does not
+# seem to support this out of the box. Warning: Depending on the platform used,
+# enabling this option may lead to badly anti-aliased labels on the edges of
+# a graph (i.e. they become hard to read).
+
+DOT_TRANSPARENT        = NO
+
+# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10)
+# support this, this feature is disabled by default.
+
+DOT_MULTI_TARGETS      = YES
+
+# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will
+# generate a legend page explaining the meaning of the various boxes and
+# arrows in the dot generated graphs.
+
+GENERATE_LEGEND        = YES
+
+# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will
+# remove the intermediate dot files that are used to generate
+# the various graphs.
+
+DOT_CLEANUP            = YES
Index: /pjproject/trunk/doc/pjsip-book/Makefile
===================================================================
--- /pjproject/trunk/doc/pjsip-book/Makefile	(revision 4704)
+++ /pjproject/trunk/doc/pjsip-book/Makefile	(revision 4704)
@@ -0,0 +1,160 @@
+# Makefile for Sphinx documentation
+#
+
+# You can set these variables from the command line.
+SPHINXOPTS    =
+SPHINXBUILD   = sphinx-build
+PAPER         =
+BUILDDIR      = _build
+
+# Internal variables.
+PAPEROPT_a4     = -D latex_paper_size=a4
+PAPEROPT_letter = -D latex_paper_size=letter
+ALLSPHINXOPTS   = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
+# the i18n builder cannot share the environment and doctrees with the others
+I18NSPHINXOPTS  = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
+
+.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext
+
+all: html
+
+help:
+	@echo "Please use \`make <target>' where <target> is one of"
+	@echo "  html       to make standalone HTML files"
+	@echo "  dirhtml    to make HTML files named index.html in directories"
+	@echo "  singlehtml to make a single large HTML file"
+	@echo "  pickle     to make pickle files"
+	@echo "  json       to make JSON files"
+	@echo "  htmlhelp   to make HTML files and a HTML help project"
+	@echo "  qthelp     to make HTML files and a qthelp project"
+	@echo "  devhelp    to make HTML files and a Devhelp project"
+	@echo "  epub       to make an epub"
+	@echo "  latex      to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
+	@echo "  latexpdf   to make LaTeX files and run them through pdflatex"
+	@echo "  text       to make text files"
+	@echo "  man        to make manual pages"
+	@echo "  texinfo    to make Texinfo files"
+	@echo "  info       to make Texinfo files and run them through makeinfo"
+	@echo "  gettext    to make PO message catalogs"
+	@echo "  changes    to make an overview of all changed/added/deprecated items"
+	@echo "  linkcheck  to check all external links for integrity"
+	@echo "  doctest    to run all doctests embedded in the documentation (if enabled)"
+
+clean:
+	-rm -rf $(BUILDDIR)/*
+	-rm -rf xml
+	-rm -rf html
+
+xml:	Doxyfile
+	doxygen
+
+html: xml
+	$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
+	@echo
+	@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
+
+dirhtml:
+	$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
+	@echo
+	@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
+
+singlehtml:
+	$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
+	@echo
+	@echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
+
+pickle:
+	$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
+	@echo
+	@echo "Build finished; now you can process the pickle files."
+
+json:
+	$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
+	@echo
+	@echo "Build finished; now you can process the JSON files."
+
+htmlhelp:
+	$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
+	@echo
+	@echo "Build finished; now you can run HTML Help Workshop with the" \
+	      ".hhp project file in $(BUILDDIR)/htmlhelp."
+
+qthelp:
+	$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
+	@echo
+	@echo "Build finished; now you can run "qcollectiongenerator" with the" \
+	      ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
+	@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/ThePJSIPBook.qhcp"
+	@echo "To view the help file:"
+	@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/ThePJSIPBook.qhc"
+
+devhelp:
+	$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
+	@echo
+	@echo "Build finished."
+	@echo "To view the help file:"
+	@echo "# mkdir -p $$HOME/.local/share/devhelp/ThePJSIPBook"
+	@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/ThePJSIPBook"
+	@echo "# devhelp"
+
+epub:
+	$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
+	@echo
+	@echo "Build finished. The epub file is in $(BUILDDIR)/epub."
+
+latex:
+	$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+	@echo
+	@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
+	@echo "Run \`make' in that directory to run these through (pdf)latex" \
+	      "(use \`make latexpdf' here to do that automatically)."
+
+latexpdf: xml
+	$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+	@echo "Running LaTeX files through pdflatex..."
+	$(MAKE) -C $(BUILDDIR)/latex all-pdf
+	@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
+
+text:
+	$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
+	@echo
+	@echo "Build finished. The text files are in $(BUILDDIR)/text."
+
+man:
+	$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
+	@echo
+	@echo "Build finished. The manual pages are in $(BUILDDIR)/man."
+
+texinfo:
+	$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
+	@echo
+	@echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
+	@echo "Run \`make' in that directory to run these through makeinfo" \
+	      "(use \`make info' here to do that automatically)."
+
+info:
+	$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
+	@echo "Running Texinfo files through makeinfo..."
+	make -C $(BUILDDIR)/texinfo info
+	@echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
+
+gettext:
+	$(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
+	@echo
+	@echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
+
+changes:
+	$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
+	@echo
+	@echo "The overview file is in $(BUILDDIR)/changes."
+
+linkcheck:
+	$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
+	@echo
+	@echo "Link check complete; look for any errors in the above output " \
+	      "or in $(BUILDDIR)/linkcheck/output.txt."
+
+doctest:
+	$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
+	@echo "Testing of doctests in the sources finished, look at the " \
+	      "results in $(BUILDDIR)/doctest/output.txt."
Index: /pjproject/trunk/doc/pjsip-book/account.rst
===================================================================
--- /pjproject/trunk/doc/pjsip-book/account.rst	(revision 4704)
+++ /pjproject/trunk/doc/pjsip-book/account.rst	(revision 4704)
@@ -0,0 +1,132 @@
+
+
+Accounts
+=========
+âAccounts provide identity (or identities) of the user who is currently using the application. An account has one SIP Uniform Resource Identifier (URI) associated with it. In SIP terms, the identity is used as the From header in outgoing requests.
+
+Account may or may not have client registration associated with it. An account is also associated with route set and some authentication credentials, which are used when sending SIP request messages using the account. An account also has presence online status, which will be reported to remote peer when they subscribe to the account's presence, or which is published to a presence server if presence publication is enabled for the account.
+
+At least one account MUST be created in the application, since any outgoing requests require an account context. If no user association is required, application can create a userless account by calling Account.create(). A userless account identifies local endpoint instead of a particular user, and it corresponds to a particular transport ID.
+
+Also one account must be set as the default account, which will be used as the account identity when pjsua fails to match the request with any accounts using the stricter matching rules.
+
+Subclassing the Account class
+---------------------------------
+To use the Account class, normally application SHOULD create its own subclass, such as::
+
+    class MyAccount : public Account
+    {
+    public:
+        MyAccount() {}
+        ~MyAccount() {}
+
+        virtual void onRegState(OnRegStateParam &prm)
+        {
+            AccountInfo ai = getInfo();
+            cout << (ai.regIsActive? "*** Register: code=" : "*** Unregister: code=")
+                 << prm.code << endl;
+
+        }
+    
+        virtual void onIncomingCall(OnIncomingCallParam &iprm)
+        {
+            Call *call = new MyCall(*this, iprm.callId);
+            // Delete the call, which will also hangup the call
+            delete call;
+        }
+    };
+
+In its subclass, application can implement the account callbacks, which is basically used to process events related to the account, such as:
+
+- the status of SIP registration
+- incoming calls
+- incoming presence subscription requests
+- incoming instant message not from buddy
+
+Application needs to override the relevant callback methods in the derived class to handle these particular events.
+
+If the events are not handled, default actions will be invoked:
+
+- incoming calls will not be handled
+- incoming presence subscription requests will be accepted
+- incoming instant messages from non-buddy will be ignored
+
+Creating Userless Accounts
+--------------------------
+A userless account identifies a particular SIP endpoint rather than a particular user. Some other SIP softphones may call this peer-to-peer mode, which means that we are calling another computer via its address rather than calling a particular user ID.
+
+So for example, we might identify ourselves as "sip:192.168.0.15" (a userless account) rather than, say, "sip:bennylp@pjsip.org".
+
+In pjsua, a userless account corresponds to a particular transport. Creating userless account is very simple, all we need is the transport ID which is returned by âEndpoint.transportCreate() method as explained in previous chapter.
+
+Here's a snippet::
+
+    AccountConfig acc_cfg;
+    acc_cfg.sipConfig.transportId = tid;
+    MyAccount *acc = new MyAccount;
+    try {
+        acc->create(acc_cfg);
+    } catch(Error& err) {
+        cout << "Account creation error: " << err.reason << endl;
+    }
+
+Once the account is created, you can use the instance as a normal account. More will be explained later.
+
+Accounts created this way will have its URI derived from the transport address. For example, if the transport address is "192.168.0.15:5080", then the account's URI for UDP transport will be "sip:192.168.0.15:5080", or "sip:192.168.0.15:5080;transport=tcp" for TCP transport.
+
+Creating Account
+----------------
+For the "normal" account, we need to configure âAccountConfig and call âAccount.create() to create the account.
+
+At the very minimum, pjsua only requires the account's ID, which is an URI to identify the account (or in SIP terms, it's called Address of Record/AOR). Here's a snippet::
+
+    AccountConfig acc_cfg;
+    acc_cfg.idUri = "sip:test1@pjsip.org";
+    MyAccount *acc = new MyAccount;
+    try {
+        acc->create(acc_cfg);
+    } catch(Error& err) {
+        cout << "Account creation error: " << err.reason << endl;
+    }
+
+The account created above doesn't do anything except to provide identity in the "From:" header for outgoing requests. The account will not register to SIP server or anything.
+
+Typically you will want the account to authenticate and register to your SIP server so that you can receive incoming calls. To do that you will need to configure some more settings in your âAccountConfig, something like this::
+
+    AccountConfig acc_cfg;
+    acc_cfg.idUri = "sip:test1@pjsip.org";
+    acc_cfg.regConfig.registrarUri = "sip:pjsip.org";
+    acc_cfg.sipConfig.authCreds.push_back( AuthCredInfo("digest", "*", "test1", 0, "test1") );
+    MyAccount *acc = new MyAccount;
+    try {
+        acc->create(acc_cfg);
+    } catch(Error& err) {
+        cout << "Account creation error: " << err.reason << endl;
+    }
+
+Account Configurations
+-----------------------
+There are many more settings that can be specified in âAccountConfig, like:
+
+- AccountRegConfig, to specify registration settings, such as registrar server and retry interval.
+- AccountSipConfig, to specify SIP settings, such as credential information and proxy server.
+- AccountCallConfig, to specify call settings, such as whether reliable provisional response (SIP 100rel) is required.
+- AccountPresConfig, to specify presence settings, such as whether presence publication (PUBLISH) is enabled.
+- AccountMwiConfig, to specify MWI (Message Waiting Indication) settings.
+- AccountNatConfig, to specify NAT settings, such as whether STUN or ICE is used.
+- AccountMediaConfig, to specify media settings, such as Secure RTP (SRTP) related settings.
+- AccountVideoConfig, to specify video settings, such as default capture and render device.
+
+Please see âAccountConfig reference documentation for more info.
+
+Account Operations
+--------------------------------------
+Some of the operations to the âAccount object:
+
+- add buddy objects
+- set account's presence online status
+- stop/start SIP registration
+
+Please see the reference documentation for Account for more info. Calls, presence, and buddy list will be explained in later sections.
+
+
Index: /pjproject/trunk/doc/pjsip-book/call.rst
===================================================================
--- /pjproject/trunk/doc/pjsip-book/call.rst	(revision 4704)
+++ /pjproject/trunk/doc/pjsip-book/call.rst	(revision 4704)
@@ -0,0 +1,103 @@
+
+
+Calls
+=====
+Calls are represented by âCall class.
+
+Subclassing the Call Class
+------------------------------------
+To use the Call class, normally application SHOULD create its own subclass, such as::
+
+    class MyCall : public Call
+    {
+    public:
+        MyCall(Account &acc, int call_id = PJSUA_INVALID_ID)
+        : Call(acc, call_id)
+        { }
+
+        ~MyCall()
+        { }
+
+        // Notification when call's state has changed.
+        virtual void onCallState(OnCallStateParam &prm);
+
+        // Notification when call's media state has changed.
+        virtual void onCallMediaState(OnCallMediaStateParam &prm);
+    };
+
+In its subclass, application can implement the call callbacks, which is basically used to process events related to the call, such as call state change or incoming call transfer request.
+
+Making Outgoing Calls
+--------------------------------------
+Making outgoing call is simple, just invoke âmakeCall() method of the Call object. Assuming you have the Account object as acc variable and destination URI string in dst_uri, you can initiate outgoing call with the snippet below::
+
+    Call *call = new MyCall(*acc);
+    CallOpParam prm(true); // Use default call settings
+    try {
+        call->makeCall(dest_uri, prm);
+    } catch(Error& err) {
+    }
+
+The snippet above creates a Call object and initiates outgoing call to dst_uri using the default call settings. Subsequent operations to the call can use the method in the âcall instance, and events to the call will be reported to the callback. More on the callback will be explained a bit later.
+
+Receiving Incoming Calls
+--------------------------------------
+Incoming calls are reported as âonIncomingCall() of the âAccount class. You must derive a class from the Account class to handle incoming calls.
+
+Below is a sample code of the callback implementation::
+
+    void MyAccount::onIncomingCall(OnIncomingCallParam &iprm)
+    {
+        Call *call = new MyCall(*this, iprm.callId);
+        CallOpParam prm;
+        prm.statusCode = (pjsip_status_code)200;
+        call->answer(prm);
+    }
+
+For incoming calls, the call instance is created in the callback parameter as shown above. Application should make sure to store the call instance during the lifetime of the call (that is until the call is disconnected).
+
+Call Properties
+-------------------
+All call properties such as state, media state, remote peer information, etc. are stored as âCallInfo class, which can be retrieved from the call object with using getInfo() method of the Call.
+
+Call Disconnection
+--------------------------------------
+Call disconnection event is a special event since once the callback that reports this event returns, the call is no longer valid and any operations invoked to the call object will raise error exception. Thus, it is recommended to delete the call object inside the callback.
+
+The call disconnection is reported in âonCallState() method of âCall and it can be detected as follows::
+
+    void MyCall::onCallState(OnCallStateParam &prm)
+    {
+        CallInfo ci = getInfo();
+        if (ci.state == PJSIP_INV_STATE_DISCONNECTED) {
+            /* Delete the call */
+            delete this;
+        }
+    }
+
+Working with Call's Audio Media
+-------------------------------------------------
+You can only operate with the call's audio media (e.g. connecting the call to the sound device in the conference bridge) when the call's audio media is ready (or active). The changes to the call's media state is reported in âonCallMediaState() callback, and if the callâs audio media is ready (or active) the function getMedia() will return a valid audio media.
+
+Below is a sample code to connect the call to the sound device when the media is active::
+
+    void MyCall::onCallMediaState(OnCallMediaStateParam &prm)
+    {
+        CallInfo ci = getInfo();
+        // Iterate all medias
+        for (unsigned i = 0; i < ci.media.size(); i++) {
+            if (getMedia(i)) { // Check if the media is valid
+                AudioMedia *aud_med = getMedia(i);
+                // Connect the call audio media to sound device
+                aud_med->startTransmit();
+                ->startTransmit(*aud_med);
+            }
+        }
+    }
+
+When the audio media becomes inactive (for example when the call is put on hold), there is no need to stop the audio media's transmission to/from the sound device since the call's audio media will be removed automatically from the conference bridge when it's no longer valid, and this will automatically remove all connections to/from the call.
+
+Call Operations
+--------------------------------------
+Some of the operations to the Call object, such as making outgoing call, answering, holding, sending re-INVITE, etc. Please see the reference documentation of Call for more info.
+
Index: /pjproject/trunk/doc/pjsip-book/conf.py
===================================================================
--- /pjproject/trunk/doc/pjsip-book/conf.py	(revision 4704)
+++ /pjproject/trunk/doc/pjsip-book/conf.py	(revision 4704)
@@ -0,0 +1,256 @@
+# -*- coding: utf-8 -*-
+#
+# The PJSIP Book documentation build configuration file, created by
+# sphinx-quickstart on Sat Nov 30 06:36:26 2013.
+#
+# This file is execfile()d with the current directory set to its containing dir.
+#
+# Note that not all possible configuration values are present in this
+# autogenerated file.
+#
+# All configuration values have a default; values that are commented out
+# serve to show the default.
+
+import sys, os
+
+# If extensions (or modules to document with autodoc) are in another directory,
+# add these directories to sys.path here. If the directory is relative to the
+# documentation root, use os.path.abspath to make it absolute, like shown here.
+#sys.path.insert(0, os.path.abspath('.'))
+
+# -- General configuration -----------------------------------------------------
+
+# If your documentation needs a minimal Sphinx version, state it here.
+#needs_sphinx = '1.0'
+
+# Add any Sphinx extension module names here, as strings. They can be extensions
+# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
+extensions = [ 'breathe', 'sphinx.ext.todo', 'sphinx.ext.viewcode']
+
+# Add any paths that contain templates here, relative to this directory.
+templates_path = ['_templates']
+
+# The suffix of source filenames.
+source_suffix = '.rst'
+
+# The encoding of source files.
+#source_encoding = 'utf-8-sig'
+
+# The master toctree document.
+master_doc = 'index'
+
+# General information about the project.
+project = u'The PJSIP Book'
+copyright = u'2013, Teluu Ltd.'
+
+# The version info for the project you're documenting, acts as replacement for
+# |version| and |release|, also used in various other places throughout the
+# built documents.
+#
+# The short X.Y version.
+version = '1.0'
+# The full version, including alpha/beta/rc tags.
+release = '1.0-beta'
+
+# The language for content autogenerated by Sphinx. Refer to documentation
+# for a list of supported languages.
+#language = None
+
+# There are two options for replacing |today|: either, you set today to some
+# non-false value, then it is used:
+#today = ''
+# Else, today_fmt is used as the format for a strftime call.
+#today_fmt = '%B %d, %Y'
+
+# List of patterns, relative to source directory, that match files and
+# directories to ignore when looking for source files.
+exclude_patterns = ['_build']
+
+# The reST default role (used for this markup: `text`) to use for all documents.
+#default_role = None
+
+# If true, '()' will be appended to :func: etc. cross-reference text.
+#add_function_parentheses = True
+
+# If true, the current module name will be prepended to all description
+# unit titles (such as .. function::).
+#add_module_names = True
+
+# If true, sectionauthor and moduleauthor directives will be shown in the
+# output. They are ignored by default.
+#show_authors = False
+
+# The name of the Pygments (syntax highlighting) style to use.
+pygments_style = 'sphinx'
+
+# A list of ignored prefixes for module index sorting.
+#modindex_common_prefix = []
+
+
+# -- Options for HTML output ---------------------------------------------------
+
+# The theme to use for HTML and HTML Help pages.  See the documentation for
+# a list of builtin themes.
+html_theme = 'default'
+
+# Theme options are theme-specific and customize the look and feel of a theme
+# further.  For a list of options available for each theme, see the
+# documentation.
+#html_theme_options = {}
+
+# Add any paths that contain custom themes here, relative to this directory.
+#html_theme_path = []
+
+# The name for this set of Sphinx documents.  If None, it defaults to
+# "<project> v<release> documentation".
+#html_title = None
+
+# A shorter title for the navigation bar.  Default is the same as html_title.
+#html_short_title = None
+
+# The name of an image file (relative to this directory) to place at the top
+# of the sidebar.
+#html_logo = None
+
+# The name of an image file (within the static path) to use as favicon of the
+# docs.  This file should be a Windows icon file (.ico) being 16x16 or 32x32
+# pixels large.
+#html_favicon = None
+
+# Add any paths that contain custom static files (such as style sheets) here,
+# relative to this directory. They are copied after the builtin static files,
+# so a file named "default.css" will overwrite the builtin "default.css".
+html_static_path = ['_static']
+
+# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
+# using the given strftime format.
+#html_last_updated_fmt = '%b %d, %Y'
+
+# If true, SmartyPants will be used to convert quotes and dashes to
+# typographically correct entities.
+#html_use_smartypants = True
+
+# Custom sidebar templates, maps document names to template names.
+#html_sidebars = {}
+
+# Additional templates that should be rendered to pages, maps page names to
+# template names.
+#html_additional_pages = {}
+
+# If false, no module index is generated.
+#html_domain_indices = True
+
+# If false, no index is generated.
+#html_use_index = True
+
+# If true, the index is split into individual pages for each letter.
+#html_split_index = False
+
+# If true, links to the reST sources are added to the pages.
+#html_show_sourcelink = True
+
+# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
+#html_show_sphinx = True
+
+# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
+#html_show_copyright = True
+
+# If true, an OpenSearch description file will be output, and all pages will
+# contain a <link> tag referring to it.  The value of this option must be the
+# base URL from which the finished HTML is served.
+#html_use_opensearch = ''
+
+# This is the file name suffix for HTML files (e.g. ".xhtml").
+#html_file_suffix = None
+
+# Output file base name for HTML help builder.
+htmlhelp_basename = 'ThePJSIPBookdoc'
+
+
+# -- Options for LaTeX output --------------------------------------------------
+
+latex_elements = {
+# The paper size ('letterpaper' or 'a4paper').
+#'papersize': 'letterpaper',
+
+# The font size ('10pt', '11pt' or '12pt').
+#'pointsize': '10pt',
+
+# Additional stuff for the LaTeX preamble.
+#'preamble': '',
+}
+
+# Grouping the document tree into LaTeX files. List of tuples
+# (source start file, target name, title, author, documentclass [howto/manual]).
+latex_documents = [
+  ('index', 'ThePJSIPBook.tex', u'The PJSIP Book Documentation',
+   u'Sauw Ming Liong, Benny Prijono', 'manual'),
+]
+
+# The name of an image file (relative to this directory) to place at the top of
+# the title page.
+#latex_logo = None
+
+# For "manual" documents, if this is true, then toplevel headings are parts,
+# not chapters.
+#latex_use_parts = False
+
+# If true, show page references after internal links.
+#latex_show_pagerefs = False
+
+# If true, show URL addresses after external links.
+#latex_show_urls = False
+
+# Documents to append as an appendix to all manuals.
+#latex_appendices = []
+
+# If false, no module index is generated.
+#latex_domain_indices = True
+
+
+# -- Options for manual page output --------------------------------------------
+
+# One entry per manual page. List of tuples
+# (source start file, name, description, authors, manual section).
+man_pages = [
+    ('index', 'thepjsipbook', u'The PJSIP Book Documentation',
+     [u'Sauw Ming Liong', u'Benny Prijono'], 1)
+]
+
+# If true, show URL addresses after external links.
+#man_show_urls = False
+
+
+# -- Options for Texinfo output ------------------------------------------------
+
+# Grouping the document tree into Texinfo files. List of tuples
+# (source start file, target name, title, author,
+#  dir menu entry, description, category)
+texinfo_documents = [
+  ('index', 'ThePJSIPBook', u'The PJSIP Book Documentation',
+   u'Sauw Ming Liong@*Benny Prijono', 'ThePJSIPBook', 'One line description of project.',
+   'Miscellaneous'),
+]
+
+# Documents to append as an appendix to all manuals.
+#texinfo_appendices = []
+
+# If false, no module index is generated.
+#texinfo_domain_indices = True
+
+# How to display URL addresses: 'footnote', 'no', or 'inline'.
+#texinfo_show_urls = 'footnote'
+
+breathe_projects = {
+   "pjsua2": "xml/",
+}
+
+breathe_default_project = "pjsua2"
+
+breathe_projects_source = {
+   "pjsua2":"../../pjsip/include/pjsua2"
+}
+
+breathe_domain_by_extension = {
+   "hpp":"cpp"
+}
Index: /pjproject/trunk/doc/pjsip-book/consider.rst
===================================================================
--- /pjproject/trunk/doc/pjsip-book/consider.rst	(revision 4704)
+++ /pjproject/trunk/doc/pjsip-book/consider.rst	(revision 4704)
@@ -0,0 +1,187 @@
+
+
+
+Development Considerations
+**************************
+
+Let's review various aspects that you need to consider when developing your application.
+
+
+Target Platforms
+================
+Platform selection will affect all aspects of development, and here we will cover  considerations for each platforms that we support.
+
+Windows Desktop
+---------------
+Windows is supported from Windows 2000 up to the recent Windows 8 and beyond. All features are expected to work. 64bit support was added recently. Development is based on Visual Studio. Considerations for this platform include:
+
+#. because Visual Studio file format keeps changing on every release, we decided to support the lowest denominator, namely Visual Studio 2005. Unfortunately the project upgrade procedure fails on Visual Studio 2010, and we don't have any solution for that. But VS 2008 and VS 20012 work.
+
+MacOS X
+-------
+All features are expected to work. Considerations include:
+
+#. development with XCode is currently not supported. This is **not** to say that you cannot use XCode, but PJSIP only provides basic Makefiles and if you want to use XCode you'd need to arrange the project yourself.
+#. Mac systems typically provides very good sound device, so we don't expect any problems with audio on Mac. 
+
+Linux Desktop
+-------------
+All features are expected to work. Linux considerations:
+
+#. use our native ALSA backend instead of PortAudio because ALSA has less jitter than OSS and our backend is more lightweight than PortAudio
+
+
+iOS for iPhone, iPad, and iPod Touch
+------------------------------------
+All features except video are expected to work (video is coming soon!). Considerations for iOS:
+
+#. you need to use TCP transport for SIP for the background feature to work
+#. IP change (for example when user is changing access point) is a feature frequently asked by developers and you can find the documentation here: http://trac.pjsip.org/repos/wiki/IPAddressChange
+#. there are some specific issues for iOS 7 and beyond, please see http://trac.pjsip.org/repos/ticket/1697
+#. if SSL is needed, you need to compile OpenSSL for iOS
+
+
+Android
+-------
+All features except video are expected to work (video is coming soon!). Considerations for Android:
+
+#. you can only use pjsua2 Java binding for this target
+#. it has been reported that Android audio device is not so good in general, so some audio tuning may be needed. Echo cancellation also needs to be checked
+#. this is also a new platform for us 
+
+
+Symbian
+-------
+Symbian has been supported for a long time but it doesn't get too many attention recently. In general all features (excluding video) are expected to work, but we're not going to do Symbian specific development anymore. Other considerations for Symbian:
+
+#. the MDA audio is not very good (it has high latency), so normally you'd want to use Audio Proxy Server (APS) or VoIP Audio Service (VAS) for the audio device, which we support. Using these audio backends will also provide us with high quality echo cancellation as well as low bitrate codecs such as AMR-NB, G.729, and iLBC. But VAS and APS requires purchase of Nokia development certificate to sign the app, and also since APS and VAS only run on specific device type, you need to package the app carefully and manage the deployment to cover various device types.
+
+
+Blackberry 10
+-------------
+Blackberry 10 is supported since PJSIP version 2.2. As this is a relatively new platform for us, we are currently listening to developer's feedback regarding the port. But so far it seems to be working well. Some considerations for BB10 platform include:
+
+#. IP change (for example when user is changing access point) is a feature frequently asked by developers and you can find the documentation here: http://trac.pjsip.org/repos/wiki/IPAddressChange
+
+
+Windows Mobile
+--------------
+This is the old Windows Mobile platform that is based on WinCE. This platform has been supported for a long time, but doesn't seem to attract any attentions recently. We expect all features except video to work, but there may be some errors every now and then because this target is not actively maintained. No new development will be done for this platform.
+
+Other considerations for Windows Mobile platform are:
+
+#. the quality of audio device on WM varies a lot, and this affects audio latency. Audio latency could go as high as hundreds of millisecond on bad hardware.
+#. echo cancellation could be a problem. We can only use basic echo suppressor due to hardware limitation, and combined with bad quality of audio device, it may cause ineffective echo cancellation. This could be mitigated by setting the audio level to low.
+
+
+Windows Phone 8
+---------------
+Windows Phone 8 (WP8) support is being added to PJSIP version 2.2 and is still under development. Specific considerations for this platform are:
+
+#. WP8 governs specific interaction with WP8 GUI and framework that needs to be followed by application in order to make VoIP call work seamlessly on the device. Some lightweight process will be created by WP8 framework in order for background call to work and PJSIP needs to put it's background processing in this process's context. Currently this feature is under development.
+
+
+
+Embedded Linux
+--------------
+In general embedded Linux support is similar to Linux and we find no problems with it. We found some specific considerations for embedded Linux as follows:
+
+#. the performance of the audio device is probably the one with most issues, as some development boards have not so descent sound device that has high audio jitter (or burst) and latency. This will affect end to end audio latency and also the performance of the echo canceller. Also we found that ALSA generally works better than OSS, so if you can have ALSA up and running that will be better. Use our native ALSA backend audio device instead of PortAudio since it is simpler and lighter.
+
+
+QNX or Other Posix Embedded OS
+------------------------------
+This is not part of our officially supported OSes, but users have run PJSIP on QNX and Blackberry 10 is based on QNX too. Since QNX provides Posix API, and maybe by using the settings found in the configure-bb10 script, PJSIP should be able to run on it, but you need to develop PJMEDIA sound device wrapper for your audio device. Other than this, we don't have enough experience to comment on the platform. 
+
+
+Other Unix Desktop OSes
+-----------------------
+Community members, including myself, have occasionally run PJSIP on other Unix OSes such as Solaris, FreeBSD, and OpenBSD. We expect PJSIP to run on these platforms (maybe with a little kick).
+
+
+Porting to Other Embedded OSes
+------------------------------
+It is possible to port PJSIP to other embedded OSes or even directly to device without OS and people have done so. In general, the closer resemblance the new OS to existing supported OS, the easier the porting job will be. The good thing is, PJSIP has been made to be very very portable, and system dependent features are localized in PJLIB and PJMEDIA audio device, so the effort is more quantifiable. Once you are able to successfully run *pjlib-test*, you are more or less there with your porting effort. Other than that, if you really want to port PJSIP to new platform, you probably already know what you're doing. 
+
+
+
+Which API to Use
+================
+
+PJSIP, PJMEDIA, and PJNATH Level
+--------------------------------
+At the lowest level we have the individual PJSIP **C** libraries, which consist of PJSIP, PJMEDIA, and PJNATH, with PJLIB-UTIL and PJLIB as support libraries. This level provides the most flexibility, but it's also the hardest to use. The only reason you'd want to use this level is if:
+
+#. you only need the individual library (say, PJNATH)
+#. you need to be very very tight in footprint (say when things need to be measured in Kilobytes instead of Megabytes)
+#. you are **not** developing a SIP client
+
+Use the corresponding PJSIP, PJMEDIA, PJNATH manuals from http://trac.pjsip.org/repos/ for information on how to use the libraries. If you use PJSIP, the PJSIP Developer's Guide (PDF) from that page provides in-depth information about PJSIP library.  
+
+PJSUA-LIB API
+-------------
+Next up is PJSUA-LIB API that combines all those libraries into a high level, integrated client user agent library written in C. This is the library that most PJSIP users use, and the highest level abstraction before pjsua2 was created. 
+
+Motivations for using PJSUA-LIB library includes:
+
+#. developing client application (PJSUA-LIB is optimized for developing client app)
+#. better efficiency than higher level API
+
+
+pjsua2 C++ API
+--------------
+pjsua2 is a new, objected oriented, C++ API created on top of PJSUA-LIB. The API is different than PJSUA-LIB, but it should be even easier to use and it should have better documentation too (such as this book). The pjsua2 API removes most cruxes typically associated with PJSIP, such as the pool and pj_str_t, and add new features such as object persistence so you can save your configs to a file, for example. All data structures are rewritten for more clarity. 
+
+A C++ application can use pjsua2 natively, while at the same time still has access to the lower level objects if it needs to. This means that the C++ application should not loose any information from using the C++ abstraction, compared to if it is using PJSUA-LIB directly. The C++ application also should not loose the ability to extend the library. It would still be able to register a custom PJSIP module, pjmedia_port, pjmedia_transport, and so on.
+
+Benefits of using pjsua2 C++ API include:
+
+#. cleaner object oriented API
+#. uniform API for higher level language such as Java and Python
+#. persistence API
+#. the ability to access PJSUA-LIB and lower level libraries when needed (including the ability to extend the libraries, for example creating custom PJSIP module, pjmedia_port, pjmedia_transport, etc.)
+
+
+Some considerations on pjsua2 C++ API are:
+#. instead of returning error, the API uses exception for error reporting
+#. pjsua2 uses standard C++ library
+#. the performance penalty due to the API abstraction should be negligible on typical modern device
+
+
+
+pjsua2 API for Java, Python, and Others
+---------------------------------------
+The pjsua2 API is also available for non-native code via SWIG binding. Configurations for Java and Python are provided with the distribution. Thanks to SWIG, other language bindings may be generated relatively easily.
+ 
+The pjsua2 API for non-native code is effectively the same as pjsua2 C++ API. However, unlike C++, you cannot access PJSUA-LIB and the underlying C libraries from the scripting language, hence you are limited to what pjsua2 provides.
+
+You can use this API if native application development is not available in target platform (such as Android), or if you prefer to develop with non-native code instead of C/C++.
+
+
+
+
+Network and Infrastructure Considerations
+=========================================
+
+NAT Issues
+----------
+
+
+TCP Requirement
+---------------
+If you support iOS devices in your service, you need to use TCP, because only TCP will work on iOS device when it is in background mode. This means your infrastructure needs to support TCP. 
+
+
+Sound Device
+============
+
+Latency
+-------
+
+
+Echo Cancellation
+-----------------
+
+
+
+
Index: /pjproject/trunk/doc/pjsip-book/endpoint.rst
===================================================================
--- /pjproject/trunk/doc/pjsip-book/endpoint.rst	(revision 4704)
+++ /pjproject/trunk/doc/pjsip-book/endpoint.rst	(revision 4704)
@@ -0,0 +1,96 @@
+
+
+Endpoint
+************
+The âEndpoint class is a singleton class, and application MUST create one and at most one of this class instance before it can do anything else. This class is the core class of PJSUA2, and it provides the following functions:
+
+- Starting up and shutting down
+- Customization of configurations, such as core UA (User Agent) SIP configuration, media configuration, and logging configuration
+
+This section will describe the functions above.
+
+To use the Endpoint class, normally application does not need to subclass it unless:
+
+- application wants to implement/override Endpointâs callback methods to get the events such as transport state change or NAT detection completion, or
+- application schedules a timer using Endpoint.utilTimerSchedule() API. In this case, application needs to implement the onTimer() callback to get the notification when the timer expires.
+
+Instantiating the Endpoint
+--------------------------
+Before anything else, you must instantiate the Endpoint class::
+
+    Endpoint *ep = new Endpoint;
+
+Once the endpoint is instantiated, you can retrieve the Endpoint instance using Endpoint.instance() static method.
+
+Creating the Library
+----------------------
+Create the library by calling its libCreate() method::
+
+    try {
+        ep->libCreate();
+    } catch(Error& err) {
+        cout << "Startup error: " << err.reason << endl;
+    }
+
+The libCreate() method will raise exception if error occurs, so we need to trap the exception using try/catch clause as above.
+
+Initializing the Library and Configuring the Settings
+----------------------------------------------------------------------------
+
+The EpConfig class provides endpoint configuration which allows the customization of the following settings:
+
+- UAConfig, to specify core SIP user agent settings.
+- MediaConfig, to specify various media settings, including ICE and TURN.
+- LogConfig, to customize logging settings.
+
+To customize the settings, create instance of EpConfig class and specify them during the endpoint initialization (will be explained more later), for example::
+
+    EpConfig ep_cfg;
+    ep_cfg.logConfig.level = 5;
+    ep_cfg.uaConfig.maxCalls = 4;
+    ep_cfg.mediaConfig.sndClockRate = 16000;
+
+Next, you can initialize the library by calling libInit()::
+
+    try {
+        EpConfig ep_cfg;
+        // Specify customization of settings in ep_cfg
+        ep->libInit(ep_cfg);
+    } catch(Error& err) {
+        cout << "Initialization error: " << err.reason << endl;
+    }
+
+The snippet above initializes the library with the default settings.
+
+Creating One or More Transports
+--------------------------------------------------
+Application needs to create one or more âtransports before it can send or receive SIP messages::
+
+    try {
+        TransportConfig tcfg;
+        tcfg.port = 5060;
+        TransportId tid = ep->transportCreate(PJSIP_TRANSPORT_UDP, tcfg);
+    } catch(Error& err) {
+        cout << "Transport creation error: " << err.reason << endl;
+    }
+
+The transportCreate() method returns the newly created âTransport ID and it takes the transport type and âTransportConfig object to customize the transport settings like bound address and listening port number. Without this, by default the transport will be bound to INADDR_ANY and any available port.
+
+There is no real use of the âTransport ID, except to create userless account (with âAccount.create(), as will be explained later), and perhaps to display the list of transports to user if the application wants it.
+
+Starting the Library
+--------------------
+Now we're ready to start the library. We need to start the library to finalize the initialization phase, e.g. to complete the initial STUN address resolution, initialize/start the sound device, etc. To start the library, call âlibStart() method::
+
+    try {
+        ep->libStart();
+    } catch(Error& err) {
+        cout << "Startup error: " << err.reason << endl;
+    }
+
+Shutting Down the Library
+--------------------------------------
+Once the application exits, the library needs to be shutdown so that resources can be released back to the operating system. This is done by deleting the Endpoint instance, which will internally call âlibDestroy()::
+
+    delete ep;
+
Index: /pjproject/trunk/doc/pjsip-book/fetch_trac.py
===================================================================
--- /pjproject/trunk/doc/pjsip-book/fetch_trac.py	(revision 4704)
+++ /pjproject/trunk/doc/pjsip-book/fetch_trac.py	(revision 4704)
@@ -0,0 +1,85 @@
+import urllib2
+import sys
+
+def fetch_rst(url):
+	print 'Fetching %s..' % url
+	req = urllib2.Request(url)
+		
+	fd = urllib2.urlopen(req, timeout=30)
+	body = fd.read()		
+
+	pos = body.find("{{{")
+	if pos >= 0:
+		body = body[pos+4:]
+	
+	pos = body.find("}}}")
+	if pos >= 0:
+		body = body[:pos]
+
+	pos = body.find("#!rst")
+	if pos >= 0:
+		body = body[pos+6:]
+
+	pos = url.rfind("/")
+	if pos >= 0:
+		filename = url[pos+1:]
+	else:
+		filename = url
+	
+	pos = filename.find('?')
+	if pos >= 0:
+		filename = filename[:pos]
+		
+	filename += ".rst"
+	f = open(filename, 'w')
+	f.write(body)
+	f.close()
+
+
+def process_index(index):
+	pages = []
+	
+	f = open(index + '.rst', 'r')
+	line = f.readline()
+	while line:
+		if line.find('toctree::') >= 0:
+			break
+		line = f.readline()
+		
+	if line.find('toctree::') < 0:
+		return []
+	# Skip directive (or whatever it's called
+	line = f.readline().strip()
+	while line and line[0] == ':':
+		line = f.readline().strip()
+	# Skip empty lines
+	line = f.readline().strip()
+	while not line:
+		line = f.readline().strip()
+	# Parse names
+	while line:
+		pages.append(line)
+		line = f.readline().strip()
+		
+	f.close()
+	
+	return pages
+
+
+if __name__ == '__main__':
+	print "** Warning: This will overwrite ALL RST files in current directory. Continue? [n] ",
+	if sys.stdin.readline().strip() != 'y':
+		sys.exit(0)
+		
+	url_format = 'http://trac.pjsip.org/repos/wiki/pjsip-doc/%s?format=txt'
+	
+	index = url_format % ('index')
+	fetch_rst(index)
+	
+	pages = process_index('index')
+	for page in pages:
+		url = url_format % (page)
+		fetch_rst(url)
+		
+	print 'Done.'
+	
Index: /pjproject/trunk/doc/pjsip-book/index.rst
===================================================================
--- /pjproject/trunk/doc/pjsip-book/index.rst	(revision 4704)
+++ /pjproject/trunk/doc/pjsip-book/index.rst	(revision 4704)
@@ -0,0 +1,36 @@
+
+.. The PJSIP Book documentation master file, created by
+   sphinx-quickstart on Sat Nov 30 06:36:26 2013.
+   You can adapt this file completely to your liking, but it should at least
+   contain the root `toctree` directive.
+
+Welcome to The PJSIP Book's documentation!
+==========================================
+
+Contents:
+
+.. toctree::
+   :maxdepth: 2
+
+   intro
+   consider
+   intro_pjsua2
+   endpoint
+   account
+   media
+   call
+   presence
+   samples
+   optimization
+   media_quality
+   network_problems
+   reference
+
+   
+Indices and tables
+==================
+
+* :ref:`genindex`
+* :ref:`modindex`
+* :ref:`search`
+
Index: /pjproject/trunk/doc/pjsip-book/intro.rst
===================================================================
--- /pjproject/trunk/doc/pjsip-book/intro.rst	(revision 4704)
+++ /pjproject/trunk/doc/pjsip-book/intro.rst	(revision 4704)
@@ -0,0 +1,33 @@
+
+
+Introduction to PJSUA2
+*******************************
+This documentation is intended for developers looking to develop Session Initiation Protocol (SIP) based client application. Some knowledge on SIP is definitely required, and of course some programming experience. Prior knowledge of PJSUA C API is not needed, although it will probably help.
+
+This PJSUA2 module provides very high level API to do SIP calls, presence, and instant messaging, as well as handling media and NAT traversal. Knowledge of SIP protocol details (such as the grammar, transaction, dialog, and whatnot) are not required, however you should know how SIP works in general and particularly how to configure SIP clients to, e.g. register to a SIP provider, specify SIP URI to make call to, and so on, to use the module.
+
+Getting Started with PJSIP
+==============================
+To begin using PJSIP
+http://trac.pjsip.org/repos/wiki/Getting-Started
+
+PJSIP Info and Documentation
+================================
+PJSIP General Wiki:
+http://trac.pjsip.org/repos/wiki
+
+PJSIP FAQ:
+http://trac.pjsip.org/repos/wiki/FAQ
+
+PJSIP Reference Manual:
+http://trac.pjsip.org/repos/wiki - Reference Manual
+
+Building PJSUA2
+=================
+PJSUA2 API declaration can be found in pjproject/pjsip/include/pjsua2 while the source codes are located in pjproject/pjsip/src/pjsua2. It will be automatically built when you compile PJSIP.
+
+PJSUA2 Language Binding Support
+===================================
+Optional if you want to:
+âSWIG (minimum version 2.0.5)
+
Index: /pjproject/trunk/doc/pjsip-book/intro_pjsua2.rst
===================================================================
--- /pjproject/trunk/doc/pjsip-book/intro_pjsua2.rst	(revision 4704)
+++ /pjproject/trunk/doc/pjsip-book/intro_pjsua2.rst	(revision 4704)
@@ -0,0 +1,158 @@
+
+PJSUA2-High Level API
+******************************
+PJSUA2 is an object-oriented abstraction above âPJSUA API. It provides high level API for constructing âSession Initiation Protocol (SIP) multimedia user agent applications (a.k.a Voice over IP/VoIP softphones). It wraps together the signaling, media, and NAT traversal functionality into easy to use call control API, account management, buddy list management, presence, and instant messaging, along with multimedia features such as local conferencing, file streaming, local playback, and voice recording, and powerful NAT traversal techniques utilizing âSTUN, âTURN, and âICE.
+
+PJSUA2 is implemented on top of PJSUA-LIB API. The SIP and media features and object modelling follows what PJSUA-LIB provides (for example, we still have accounts, call, buddy, and so on), but the API to access them is different. These features will be described later in this chapter. PJSUA2 is a C++ library, which you can find under ``pjsip`` directory in the PJSIP distribution. The C++ library can be used by native C++ applications directly. But PJSUA2 is not just a C++ library. From the beginning, it has been designed to be accessible from high level non-native languages such as Java and Python. This is achieved by SWIG binding. And thanks to SWIG, binding to other languages can be added relatively easily in the future.
+
+
+PJSUA2 Main Classes
+======================
+Here are the main classes of the PJSUA2:
+
+Endpoint
+--------------
+This is the main class of PJSUA2. You need to instantiate one and exactly one of this class, and from the instance you can then initialize and start the library.
+
+Account
+-------------
+An account specifies the identity of the person (or endpoint) on one side of SIP conversation. At least one account instance needs to be created before anything else, and from the account instance you can start making/receiving calls as well as adding buddies.
+
+Media
+----------
+This class represents a media element which is capable to either produce media or takes media.
+
+Call
+------
+This class is used to manipulate calls, such as to answer the call, hangup the call, put the call on hold, transfer the call, etc.
+
+Buddy
+---------
+This class represents a remote buddy (a person, or a SIP endpoint). You can subscribe to presence status of a buddy to know whether the buddy is online/offline/etc., and you can send and receive instant messages to/from the buddy.
+
+General Concepts
+==================
+Class Usage Patterns
+---------------------
+With the methods of the main classes above, you will be able to invoke various operations to the object quite easily. But how can we get events/notifications from these classes? Each of the main classes above (except Media) will get their events in the callback methods. So to handle these events, just derive a class from the corresponding class (âEndpoint, Call, Account, or Buddy) and implement/override the relevant method (depending on which event you want to handle). More will be explained in later sections.
+
+Objects Persistence
+---------------------
+PJSUA2 includes PersistentObject class to provide functionality to read/write data from/to a document (string or file). The data can be simple data types such as boolean, number, string, and string arrays, or a user defined object. Currently the implementation supports reading and writing from/to JSON document, but the framework allows application to extend the API to support other document formats.
+
+As such, classes which inherit from PersistentObject, such as EpConfig (endpoint configuration), AccountConfig (account configuration), and BuddyConfig (buddy configuration) can be loaded/saved from/to a file. Hereâs an example to save a config to a file:
+
+.. code-block:: c++
+
+    EpConfig epCfg;
+    JsonDocument jDoc;
+    epCfg.uaConfig.maxCalls = 61;
+    epCfg.uaConfig.userAgent = "Just JSON Test";
+    jDoc.writeObject(epCfg);
+    jDoc.saveFile(âjsontest.jsâ);
+
+To load from the file:
+
+.. code-block:: c++
+
+    EpConfig epCfg;
+    JsonDocument jDoc;
+    jDoc.loadFile("jsontest.js");
+    jDoc.readObject(epCfg);
+
+Error Handling
+---------------
+We use exceptions as means to report error, as this would make the program flows more naturally. Operations which yield error will raise âError exception. If you prefer to display the error in more structured manner, the âError class has several members to explain the error, such as the operation name that raised the error, the error code, and the error message itself.
+
+Asynchronous Operations
+-------------------------
+If you have developed applications with PJSIP, you'll know about this already. In PJSIP, all operations that involve sending and receiving SIP messages are asynchronous, meaning that the function that invokes the operation will complete immediately, and you will be given the completion status as callbacks.
+
+Take a look for example the âmakeCall() method of the Call class. This function is used to initiate outgoing call to a destination. When this function returns successfully, it does not mean that the call has been established, but rather it means that the call has been initiated successfully. You will be given the report of the call progress and/or completion in the âonCallState() callback method of âCall class.
+
+Threading
+----------
+For platforms that require polling, the PJSUA2 module provides its own worker thread to poll PJSIP, so it is not necessary to instantiate own your polling thread. Having said that the application should be prepared to have the callbacks called by different thread than the main thread. The PJSUA2 module should be thread safe.
+
+
+Using in C++ Application
+========================
+As mentioned in previous chapter, A C++ application can use *pjsua2* natively, while at the same time still has access to the lower level objects and the ability to extend the libraries if it needs to. Using the API will be exactly the same as the API reference that is written in this book.
+
+Here is a sample complete C++ application to give you some idea about the API. The snippet below initializes the library and creates an account that registers to our pjsip.org SIP server.
+
+.. code-block:: c++
+    
+  #include <pjsua2.hpp>
+  #include <iostream>
+  
+  using namespace pj;
+  
+  // Subclass to extend the Account and get notifications etc.
+  class MyAccount : public Account {
+  public:
+      virtual void onRegState(OnRegStateParam &prm) {
+          AccountInfo ai = getInfo();
+          std::cout << (ai.regIsActive? "*** Register:" : "*** Unregister:")
+                    << " code=" << prm.code << std::endl;
+      }
+  };
+
+  int main()
+  {
+      Endpoint ep;
+      
+      ep.libCreate();
+      
+      // Initialize endpoint
+      EpConfig ep_cfg;
+      ep_cfg.uaConfig.userAgent = "pjsua2-hello";
+      ep.libInit( ep_cfg );
+      
+      // Create SIP transport. Error handling sample is shown
+      TransportConfig tcfg;
+      tcfg.port = 5060;
+      try {
+          ep.transportCreate(PJSIP_TRANSPORT_UDP, tcfg);
+      } catch (Error &err) {
+          std::cout << err.info() << std::endl;
+          return 1;
+      }
+      
+      // Start the library (worker threads etc)
+      ep.libStart();
+      std::cout << "*** PJSUA2 STARTED ***" << std::endl;
+      
+      // Configure an AccountConfig
+      AccountConfig acfg;
+      acfg.idUri = "sip:test@pjsip.org";
+      acfg.regConfig.registrarUri = "sip:pjsip.org";
+      AuthCredInfo cred("digest", "*", "test", 0, "secret");
+      acfg.sipConfig.authCreds.push_back( cred );
+      
+      // Create the account
+      MyAccount *acc = new MyAccount;
+      acc->create(acfg);
+      
+      // Here we don't have anything else to do..
+      pj_thread_sleep(10000);
+      
+      // Delete the account. This will unregister from server
+      delete acc;
+      
+      // This will implicitly shutdown the library
+      return 0;
+  }
+
+
+Using in Python Application
+===========================
+
+
+
+Using in Java Application
+=========================
+
+
+
+
Index: /pjproject/trunk/doc/pjsip-book/make.bat
===================================================================
--- /pjproject/trunk/doc/pjsip-book/make.bat	(revision 4704)
+++ /pjproject/trunk/doc/pjsip-book/make.bat	(revision 4704)
@@ -0,0 +1,190 @@
+@ECHO OFF
+
+REM Command file for Sphinx documentation
+
+if "%SPHINXBUILD%" == "" (
+	set SPHINXBUILD=sphinx-build
+)
+set BUILDDIR=_build
+set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% .
+set I18NSPHINXOPTS=%SPHINXOPTS% .
+if NOT "%PAPER%" == "" (
+	set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
+	set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS%
+)
+
+if "%1" == "" goto help
+
+if "%1" == "help" (
+	:help
+	echo.Please use `make ^<target^>` where ^<target^> is one of
+	echo.  html       to make standalone HTML files
+	echo.  dirhtml    to make HTML files named index.html in directories
+	echo.  singlehtml to make a single large HTML file
+	echo.  pickle     to make pickle files
+	echo.  json       to make JSON files
+	echo.  htmlhelp   to make HTML files and a HTML help project
+	echo.  qthelp     to make HTML files and a qthelp project
+	echo.  devhelp    to make HTML files and a Devhelp project
+	echo.  epub       to make an epub
+	echo.  latex      to make LaTeX files, you can set PAPER=a4 or PAPER=letter
+	echo.  text       to make text files
+	echo.  man        to make manual pages
+	echo.  texinfo    to make Texinfo files
+	echo.  gettext    to make PO message catalogs
+	echo.  changes    to make an overview over all changed/added/deprecated items
+	echo.  linkcheck  to check all external links for integrity
+	echo.  doctest    to run all doctests embedded in the documentation if enabled
+	goto end
+)
+
+if "%1" == "clean" (
+	for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i
+	del /q /s %BUILDDIR%\*
+	goto end
+)
+
+if "%1" == "html" (
+	%SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html
+	if errorlevel 1 exit /b 1
+	echo.
+	echo.Build finished. The HTML pages are in %BUILDDIR%/html.
+	goto end
+)
+
+if "%1" == "dirhtml" (
+	%SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml
+	if errorlevel 1 exit /b 1
+	echo.
+	echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml.
+	goto end
+)
+
+if "%1" == "singlehtml" (
+	%SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml
+	if errorlevel 1 exit /b 1
+	echo.
+	echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml.
+	goto end
+)
+
+if "%1" == "pickle" (
+	%SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle
+	if errorlevel 1 exit /b 1
+	echo.
+	echo.Build finished; now you can process the pickle files.
+	goto end
+)
+
+if "%1" == "json" (
+	%SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json
+	if errorlevel 1 exit /b 1
+	echo.
+	echo.Build finished; now you can process the JSON files.
+	goto end
+)
+
+if "%1" == "htmlhelp" (
+	%SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp
+	if errorlevel 1 exit /b 1
+	echo.
+	echo.Build finished; now you can run HTML Help Workshop with the ^
+.hhp project file in %BUILDDIR%/htmlhelp.
+	goto end
+)
+
+if "%1" == "qthelp" (
+	%SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp
+	if errorlevel 1 exit /b 1
+	echo.
+	echo.Build finished; now you can run "qcollectiongenerator" with the ^
+.qhcp project file in %BUILDDIR%/qthelp, like this:
+	echo.^> qcollectiongenerator %BUILDDIR%\qthelp\ThePJSIPBook.qhcp
+	echo.To view the help file:
+	echo.^> assistant -collectionFile %BUILDDIR%\qthelp\ThePJSIPBook.ghc
+	goto end
+)
+
+if "%1" == "devhelp" (
+	%SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp
+	if errorlevel 1 exit /b 1
+	echo.
+	echo.Build finished.
+	goto end
+)
+
+if "%1" == "epub" (
+	%SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub
+	if errorlevel 1 exit /b 1
+	echo.
+	echo.Build finished. The epub file is in %BUILDDIR%/epub.
+	goto end
+)
+
+if "%1" == "latex" (
+	%SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
+	if errorlevel 1 exit /b 1
+	echo.
+	echo.Build finished; the LaTeX files are in %BUILDDIR%/latex.
+	goto end
+)
+
+if "%1" == "text" (
+	%SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text
+	if errorlevel 1 exit /b 1
+	echo.
+	echo.Build finished. The text files are in %BUILDDIR%/text.
+	goto end
+)
+
+if "%1" == "man" (
+	%SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man
+	if errorlevel 1 exit /b 1
+	echo.
+	echo.Build finished. The manual pages are in %BUILDDIR%/man.
+	goto end
+)
+
+if "%1" == "texinfo" (
+	%SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo
+	if errorlevel 1 exit /b 1
+	echo.
+	echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo.
+	goto end
+)
+
+if "%1" == "gettext" (
+	%SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale
+	if errorlevel 1 exit /b 1
+	echo.
+	echo.Build finished. The message catalogs are in %BUILDDIR%/locale.
+	goto end
+)
+
+if "%1" == "changes" (
+	%SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes
+	if errorlevel 1 exit /b 1
+	echo.
+	echo.The overview file is in %BUILDDIR%/changes.
+	goto end
+)
+
+if "%1" == "linkcheck" (
+	%SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck
+	if errorlevel 1 exit /b 1
+	echo.
+	echo.Link check complete; look for any errors in the above output ^
+or in %BUILDDIR%/linkcheck/output.txt.
+	goto end
+)
+
+if "%1" == "doctest" (
+	%SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest
+	if errorlevel 1 exit /b 1
+	echo.
+	echo.Testing of doctests in the sources finished, look at the ^
+results in %BUILDDIR%/doctest/output.txt.
+	goto end
+)
+
+:end
Index: /pjproject/trunk/doc/pjsip-book/media.rst
===================================================================
--- /pjproject/trunk/doc/pjsip-book/media.rst	(revision 4704)
+++ /pjproject/trunk/doc/pjsip-book/media.rst	(revision 4704)
@@ -0,0 +1,113 @@
+
+
+Media
+=====
+Media objects are objects that are capable to either produce media or takes media. In âPJMEDIA terms, these objects are implemented as media ports (âpjmedia_port).
+
+An important subclass of Media is AudioMedia which represents audio media. There are several type of audio media objects supported in PJSUA2:
+
+- CallAudioMedia, to transmit and receive audio to/from remote person.
+- AudioMediaPlayer, to play WAV file(s).
+- AudioMediaRecorder, to record audio to a WAV file.
+
+More media objects may be added in the future.
+
+The Audio Conference Bridge
+----------------------------
+The conference bridge provides a simple but yet powerful concept to manage audio flow between the audio medias. The principle is very simple, that is you connect audio source to audio destination, and the bridge will make the audio flows from the source to destination, and that's it. If more than one sources are transmitting to the same destination, then the audio from the sources will be mixed. If one source is transmitting to more than one destinations, the bridge will take care of duplicating the audio from the source to the multiple destinations.
+
+In âPJSUA2, all audio media objects are plugged-in to the central conference bridge for easier manipulation. A plugged-in audio media will not be connected to anything, so media will not flow from/to any objects. An audio media source can start/stop the transmission to a destination by using the API AudioMedia.startTransmit() / AudioMedia.stopTransmit().
+
+An audio media object plugged-in to the conference bridge will be given a port ID number that identifies the object in the bridge. Application can use the API AudioMedia.getPortId() to retrieve the port ID. Normally, application should not need to worry about the port ID (as all will be taken care of by the bridge) unless application want to create its own custom audio media.
+
+Playing a WAV File
+++++++++++++++++++
+To playback the WAV file to the speaker, just start the transmission of the WAV playback object to the sound device::
+
+    AudioMediaPlayer player;
+    try {
+        player.createPlayer(âfile.wavâ);
+        player.startTransmit();
+    } catch(Error& err) {
+    }
+
+Once you're done with the playback, just stop the transmission n to stop the playback::
+
+    try {
+        player.stopTransmit();
+    } catch(Error& err) {
+    }
+
+Recording to WAV File
++++++++++++++++++++++
+Or if you want to record the microphone to the WAV file, simply do this::
+
+    AudioMediaRecorder recorder;
+    try {
+        recorder.createRecorder(âfile.wavâ);
+        .startTransmit(recorder);
+    } catch(Error& err) {
+    }
+
+And the media will flow from the sound device to the WAV record file. As usual, to stop or pause recording, just stop the transmission::
+
+    try {
+       .stopTransmit(recorder);
+    } catch(Error& err) {
+    }
+
+(Note that stopping the transmission to the WAV recorder as above does not close the WAV file, and you can resume recording by connecting a source to the WAV recorder again. You cannot playback the recorded WAV file before you close it.)
+
+Looping Audio
++++++++++++++
+If you want, you can loop the audio of a media object to itself (i.e. the audio received from the object will be transmitted to itself). For example, you can loop the audio of the sound device with::
+
+    .startTransmit();
+
+With the above connection, audio received from the microphone will be played back to the speaker. This is useful to test whether the microphone and speaker are working properly.
+
+You can loop-back audio from any objects, as long as the object has bidirectional media. That means you can loop the call's audio media, so that audio received from the remote person will be transmitted back to her/him. But you can't loop the WAV player or recorder since these objects can only play or record and not both.
+
+Normal Call
++++++++++++
+
+A single call can has several audio medias. Application can retrieve the audio media by using the API Call.getMedia()::
+
+    AudioMedia *aud_med = (AudioMedia *)call.getMedia(0);
+
+In the above, we assume that the audio media is located in index 0. Of course on a real application, we should iterate the medias to find the correct media index by checking whether the media returned by getMedia() is valid. More on this will be explained later in the Call section. Then for a normal call, we would want to establish bidirectional audio with the remote person, which can be done easily by connecting the sound device and the call audio media and vice versa::
+
+    // This will connect the sound device/mic to the call audio media
+    ->startTransmit(*aud_med);
+
+    // And this will connect the call audio media to the sound device/speaker
+    aud_med->startTransmit();
+
+Second Call
++++++++++++
+Suppose we want to talk with two remote parties at the same time. Since we already have bidirectional media connection with one party, we just need to add bidirectional connection with the other party using the code below::
+
+    AudioMedia *aud_med2 = (AudioMedia *)call2.getMedia(0);
+    ->startTransmit(*aud_med2);
+    aud_med2->startTransmit();
+
+Now we can talk to both parties at the same time, and we will hear audio from either party. But at this stage, the remote parties can't talk or hear each other (i.e. we're not in full conference mode yet).
+
+Conference Call
++++++++++++++++
+To enable both parties talk to each other, just establish bidirectional media between them::
+
+    aud_med->startTransmit(*aud_med2);
+    aud_med2->startTransmit(*aud_med);
+
+Now the three parties (us and both remote parties) will be able to talk to each other.
+
+Recording the Conference
+++++++++++++++++++++++++
+
+While doing the conference, it perfectly makes sense to want to record the conference to a WAV file, and all we need to do is to connect the microphone and both calls to the WAV recorder::
+
+    ->startTransmit(recorder);
+    aud_med->startTransmit(recorder);
+    aud_med2->startTransmit(recorder);
+
Index: /pjproject/trunk/doc/pjsip-book/media_quality.rst
===================================================================
--- /pjproject/trunk/doc/pjsip-book/media_quality.rst	(revision 4704)
+++ /pjproject/trunk/doc/pjsip-book/media_quality.rst	(revision 4704)
@@ -0,0 +1,11 @@
+
+
+Media Quality
+*************
+
+Audio Quality
+=============
+
+Video Quality
+=============
+
Index: /pjproject/trunk/doc/pjsip-book/network_problems.rst
===================================================================
--- /pjproject/trunk/doc/pjsip-book/network_problems.rst	(revision 4704)
+++ /pjproject/trunk/doc/pjsip-book/network_problems.rst	(revision 4704)
@@ -0,0 +1,11 @@
+
+
+Network Problems
+****************
+
+IP Address Change
+=================
+
+Blocked/Filtered Network
+========================
+
Index: /pjproject/trunk/doc/pjsip-book/optimization.rst
===================================================================
--- /pjproject/trunk/doc/pjsip-book/optimization.rst	(revision 4704)
+++ /pjproject/trunk/doc/pjsip-book/optimization.rst	(revision 4704)
@@ -0,0 +1,11 @@
+
+
+General Configuration Optimization
+**********************************
+
+PJSUA2 Settings
+===============
+
+CPU Optimization
+================
+
Index: /pjproject/trunk/doc/pjsip-book/presence.rst
===================================================================
--- /pjproject/trunk/doc/pjsip-book/presence.rst	(revision 4704)
+++ /pjproject/trunk/doc/pjsip-book/presence.rst	(revision 4704)
@@ -0,0 +1,43 @@
+
+
+Buddy (Presence)
+================
+This class represents a remote buddy (a person, or a SIP endpoint).
+To use the Buddy class, application DOES NOT need to subclass it unless application wants to get the notifications on buddy state change.
+
+Subscribe to Buddy's Presence Status
+---------------------------------------------------------
+To subscribe to buddy's presence status, you need to add a buddy object, install callback to handle buddy's event, and start subscribing to buddy's presence status. The snippet below shows a sample code to achieve these::
+
+  class MyBuddyCallback(pjsua.BuddyCallback):
+    def __init__(self, buddy=None):
+        pjsua.BuddyCallback.__init__(self, buddy)
+
+    def on_state(self):
+        print "Buddy", self.buddy.info().uri, "is",
+        print self.buddy.info().online_text
+
+  try:
+    uri = '"Alice" <sip:alice@example.com>'
+    buddy = acc.add_buddy(uri, cb=MyBuddyCallback())
+    buddy.subscribe()
+
+  except pjsua.Error, err:
+    print 'Error adding buddy:', err
+
+For more information please see âBuddy class and âBuddyCallback class reference documentation.
+
+Responding to Presence Subscription Request
+
+By default, incoming presence subscription to an account will be accepted automatically. You will probably want to change this behavior, for example only to automatically accept subscription if it comes from one of the buddy in the buddy list, and for anything else prompt the user if he/she wants to accept the request.
+
+This can be done by implementing the âon_incoming_subscribe() method of the âAccountCallback class.
+
+Changing Account's Presence Status
+
+The âAccount class provides two methods to change account's presence status:
+
+âset_basic_status() can be used to set basic account's presence status (i.e. available or not available).
+âset_presence_status() can be used to set both the basic presence status and some extended information (e.g. busy, away, on the phone, etc.).
+When the presence status is changed, the account will publish the new status to all of its presence subscriber, either with PUBLISH request or SUBSCRIBE request, or both, depending on account configuration.
+
Index: /pjproject/trunk/doc/pjsip-book/reference.rst
===================================================================
--- /pjproject/trunk/doc/pjsip-book/reference.rst	(revision 4704)
+++ /pjproject/trunk/doc/pjsip-book/reference.rst	(revision 4704)
@@ -0,0 +1,20 @@
+
+
+PJSUA2 API Reference Manuals
+****************************
+
+Endpoint
+========
+
+Account
+=======
+
+Media
+=====
+
+Call
+====
+
+Buddy
+=====
+
Index: /pjproject/trunk/doc/pjsip-book/samples.rst
===================================================================
--- /pjproject/trunk/doc/pjsip-book/samples.rst	(revision 4704)
+++ /pjproject/trunk/doc/pjsip-book/samples.rst	(revision 4704)
@@ -0,0 +1,27 @@
+
+
+PJSUA2 Sample Applications
+***********************************
+
+Sample Apps
+===========
+
+Python GUI
+------------------
+It requires Python 2.7 and above.
+
+Android & Java
+-----------------------------
+
+C++
+-----------------------------
+
+Miscellaneous
+===================
+
+How to dump call stats
+-----------------------------
+
+How to âŠ
+-----------------------------
+
Index: /pjproject/trunk/pjlib-util/build/Makefile
===================================================================
--- /pjproject/trunk/pjlib-util/build/Makefile	(revision 4703)
+++ /pjproject/trunk/pjlib-util/build/Makefile	(revision 4704)
@@ -39,5 +39,5 @@
 		base64.o cli.o cli_console.o cli_telnet.o crc32.o errno.o dns.o \
 		dns_dump.o dns_server.o getopt.o hmac_md5.o hmac_sha1.o \
-		http_client.o md5.o pcap.o resolver.o scanner.o sha1.o \
+		http_client.o json.o md5.o pcap.o resolver.o scanner.o sha1.o \
 		srv_resolver.o string.o stun_simple.o \
 		stun_simple_client.o xml.o
@@ -51,5 +51,5 @@
 export UTIL_TEST_SRCDIR = ../src/pjlib-util-test
 export UTIL_TEST_OBJS += xml.o encryption.o stun.o resolver_test.o test.o \
-		http_client.o
+		json_test.o http_client.o
 export UTIL_TEST_CFLAGS += $(_CFLAGS)
 export UTIL_TEST_CXXFLAGS += $(_CXXFLAGS)
Index: /pjproject/trunk/pjlib-util/build/pjlib_util.vcproj
===================================================================
--- /pjproject/trunk/pjlib-util/build/pjlib_util.vcproj	(revision 4703)
+++ /pjproject/trunk/pjlib-util/build/pjlib_util.vcproj	(revision 4704)
@@ -286,4 +286,136 @@
 		</Configuration>
 		<Configuration
+			Name="Release|Windows Mobile 6 Standard SDK (ARMV4I)"
+			ConfigurationType="4"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-release-defaults.vsprops"
+			ATLMinimizesCRunTimeLibraryUsage="false"
+			CharacterSet="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				ExecutionBucket="7"
+				AdditionalIncludeDirectories="../include,../../pjlib/include"
+				PreprocessorDefinitions="_LIB;"
+				PrecompiledHeaderFile=""
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLibrarianTool"
+				OutputFile="..\lib\pjlib-util-$(TargetCPU)-wm6std-vc$(VSVer)-$(ConfigurationName).lib"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCCodeSignTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+			<DeploymentTool
+				ForceDirty="-1"
+				RemoteDirectory=""
+				RegisterOutput="0"
+				AdditionalFiles=""
+			/>
+			<DebuggerTool
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release|Windows Mobile 6 Professional SDK (ARMV4I)"
+			ConfigurationType="4"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-release-defaults.vsprops"
+			ATLMinimizesCRunTimeLibraryUsage="false"
+			CharacterSet="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				ExecutionBucket="7"
+				AdditionalIncludeDirectories="../include,../../pjlib/include"
+				PreprocessorDefinitions="_LIB;"
+				PrecompiledHeaderFile=""
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLibrarianTool"
+				OutputFile="..\lib\pjlib-util-$(TargetCPU)-wm6pro-vc$(VSVer)-$(ConfigurationName).lib"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCCodeSignTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+			<DeploymentTool
+				ForceDirty="-1"
+				RemoteDirectory=""
+				RegisterOutput="0"
+				AdditionalFiles=""
+			/>
+			<DebuggerTool
+			/>
+		</Configuration>
+		<Configuration
 			Name="Debug|Win32"
 			ConfigurationType="4"
@@ -536,4 +668,136 @@
 		</Configuration>
 		<Configuration
+			Name="Debug|Windows Mobile 6 Standard SDK (ARMV4I)"
+			ConfigurationType="4"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-common-defaults.vsprops"
+			ATLMinimizesCRunTimeLibraryUsage="false"
+			CharacterSet="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				ExecutionBucket="7"
+				AdditionalIncludeDirectories="../include,../../pjlib/include"
+				PreprocessorDefinitions="_LIB;"
+				PrecompiledHeaderFile=""
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLibrarianTool"
+				OutputFile="..\lib\pjlib-util-$(TargetCPU)-wm6std-vc$(VSVer)-$(ConfigurationName).lib"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCCodeSignTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+			<DeploymentTool
+				ForceDirty="-1"
+				RemoteDirectory=""
+				RegisterOutput="0"
+				AdditionalFiles=""
+			/>
+			<DebuggerTool
+			/>
+		</Configuration>
+		<Configuration
+			Name="Debug|Windows Mobile 6 Professional SDK (ARMV4I)"
+			ConfigurationType="4"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-common-defaults.vsprops"
+			ATLMinimizesCRunTimeLibraryUsage="false"
+			CharacterSet="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				ExecutionBucket="7"
+				AdditionalIncludeDirectories="../include,../../pjlib/include"
+				PreprocessorDefinitions="_LIB;"
+				PrecompiledHeaderFile=""
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLibrarianTool"
+				OutputFile="..\lib\pjlib-util-$(TargetCPU)-wm6pro-vc$(VSVer)-$(ConfigurationName).lib"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCCodeSignTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+			<DeploymentTool
+				ForceDirty="-1"
+				RemoteDirectory=""
+				RegisterOutput="0"
+				AdditionalFiles=""
+			/>
+			<DebuggerTool
+			/>
+		</Configuration>
+		<Configuration
 			Name="Debug-Static|Win32"
 			ConfigurationType="4"
@@ -786,4 +1050,136 @@
 		</Configuration>
 		<Configuration
+			Name="Debug-Static|Windows Mobile 6 Standard SDK (ARMV4I)"
+			ConfigurationType="4"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-common-defaults.vsprops"
+			ATLMinimizesCRunTimeLibraryUsage="false"
+			CharacterSet="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				ExecutionBucket="7"
+				AdditionalIncludeDirectories="../include,../../pjlib/include"
+				PreprocessorDefinitions="_LIB;"
+				PrecompiledHeaderFile=""
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLibrarianTool"
+				OutputFile="..\lib\pjlib-util-$(TargetCPU)-wm6std-vc$(VSVer)-$(ConfigurationName).lib"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCCodeSignTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+			<DeploymentTool
+				ForceDirty="-1"
+				RemoteDirectory=""
+				RegisterOutput="0"
+				AdditionalFiles=""
+			/>
+			<DebuggerTool
+			/>
+		</Configuration>
+		<Configuration
+			Name="Debug-Static|Windows Mobile 6 Professional SDK (ARMV4I)"
+			ConfigurationType="4"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-common-defaults.vsprops"
+			ATLMinimizesCRunTimeLibraryUsage="false"
+			CharacterSet="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				ExecutionBucket="7"
+				AdditionalIncludeDirectories="../include,../../pjlib/include"
+				PreprocessorDefinitions="_LIB;"
+				PrecompiledHeaderFile=""
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLibrarianTool"
+				OutputFile="..\lib\pjlib-util-$(TargetCPU)-wm6pro-vc$(VSVer)-$(ConfigurationName).lib"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCCodeSignTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+			<DeploymentTool
+				ForceDirty="-1"
+				RemoteDirectory=""
+				RegisterOutput="0"
+				AdditionalFiles=""
+			/>
+			<DebuggerTool
+			/>
+		</Configuration>
+		<Configuration
 			Name="Release-Dynamic|Win32"
 			ConfigurationType="4"
@@ -1035,4 +1431,136 @@
 		</Configuration>
 		<Configuration
+			Name="Release-Dynamic|Windows Mobile 6 Standard SDK (ARMV4I)"
+			ConfigurationType="4"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-release-defaults.vsprops"
+			ATLMinimizesCRunTimeLibraryUsage="false"
+			CharacterSet="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				ExecutionBucket="7"
+				AdditionalIncludeDirectories="../include,../../pjlib/include"
+				PreprocessorDefinitions="_LIB;"
+				PrecompiledHeaderFile=""
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLibrarianTool"
+				OutputFile="..\lib\pjlib-util-$(TargetCPU)-wm6std-vc$(VSVer)-$(ConfigurationName).lib"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCCodeSignTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+			<DeploymentTool
+				ForceDirty="-1"
+				RemoteDirectory=""
+				RegisterOutput="0"
+				AdditionalFiles=""
+			/>
+			<DebuggerTool
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release-Dynamic|Windows Mobile 6 Professional SDK (ARMV4I)"
+			ConfigurationType="4"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-release-defaults.vsprops"
+			ATLMinimizesCRunTimeLibraryUsage="false"
+			CharacterSet="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				ExecutionBucket="7"
+				AdditionalIncludeDirectories="../include,../../pjlib/include"
+				PreprocessorDefinitions="_LIB;"
+				PrecompiledHeaderFile=""
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLibrarianTool"
+				OutputFile="..\lib\pjlib-util-$(TargetCPU)-wm6pro-vc$(VSVer)-$(ConfigurationName).lib"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCCodeSignTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+			<DeploymentTool
+				ForceDirty="-1"
+				RemoteDirectory=""
+				RegisterOutput="0"
+				AdditionalFiles=""
+			/>
+			<DebuggerTool
+			/>
+		</Configuration>
+		<Configuration
 			Name="Debug-Dynamic|Win32"
 			ConfigurationType="4"
@@ -1285,4 +1813,136 @@
 		</Configuration>
 		<Configuration
+			Name="Debug-Dynamic|Windows Mobile 6 Standard SDK (ARMV4I)"
+			ConfigurationType="4"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-common-defaults.vsprops"
+			ATLMinimizesCRunTimeLibraryUsage="false"
+			CharacterSet="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				ExecutionBucket="7"
+				AdditionalIncludeDirectories="../include,../../pjlib/include"
+				PreprocessorDefinitions="_LIB;"
+				PrecompiledHeaderFile=""
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLibrarianTool"
+				OutputFile="..\lib\pjlib-util-$(TargetCPU)-wm6std-vc$(VSVer)-$(ConfigurationName).lib"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCCodeSignTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+			<DeploymentTool
+				ForceDirty="-1"
+				RemoteDirectory=""
+				RegisterOutput="0"
+				AdditionalFiles=""
+			/>
+			<DebuggerTool
+			/>
+		</Configuration>
+		<Configuration
+			Name="Debug-Dynamic|Windows Mobile 6 Professional SDK (ARMV4I)"
+			ConfigurationType="4"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-common-defaults.vsprops"
+			ATLMinimizesCRunTimeLibraryUsage="false"
+			CharacterSet="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				ExecutionBucket="7"
+				AdditionalIncludeDirectories="../include,../../pjlib/include"
+				PreprocessorDefinitions="_LIB;"
+				PrecompiledHeaderFile=""
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLibrarianTool"
+				OutputFile="..\lib\pjlib-util-$(TargetCPU)-wm6pro-vc$(VSVer)-$(ConfigurationName).lib"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCCodeSignTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+			<DeploymentTool
+				ForceDirty="-1"
+				RemoteDirectory=""
+				RegisterOutput="0"
+				AdditionalFiles=""
+			/>
+			<DebuggerTool
+			/>
+		</Configuration>
+		<Configuration
 			Name="Release-Static|Win32"
 			ConfigurationType="4"
@@ -1534,9 +2194,9 @@
 		</Configuration>
 		<Configuration
-			Name="Release|Windows Mobile 6 Standard SDK (ARMV4I)"
-			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-release-defaults.vsprops"
+			Name="Release-Static|Windows Mobile 6 Standard SDK (ARMV4I)"
+			ConfigurationType="4"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-release-defaults.vsprops"
 			ATLMinimizesCRunTimeLibraryUsage="false"
 			CharacterSet="1"
-			ConfigurationType="4"
 			>
 			<Tool
@@ -1557,7 +2217,7 @@
 			<Tool
 				Name="VCCLCompilerTool"
-				PreprocessorDefinitions="_LIB;"
 				ExecutionBucket="7"
 				AdditionalIncludeDirectories="../include,../../pjlib/include"
+				PreprocessorDefinitions="_LIB;"
 				PrecompiledHeaderFile=""
 			/>
@@ -1600,603 +2260,9 @@
 		</Configuration>
 		<Configuration
-			Name="Release|Windows Mobile 6 Professional SDK (ARMV4I)"
-			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-release-defaults.vsprops"
-			ATLMinimizesCRunTimeLibraryUsage="false"
-			CharacterSet="1"
+			Name="Release-Static|Windows Mobile 6 Professional SDK (ARMV4I)"
 			ConfigurationType="4"
-			>
-			<Tool
-				Name="VCPreBuildEventTool"
-			/>
-			<Tool
-				Name="VCCustomBuildTool"
-			/>
-			<Tool
-				Name="VCXMLDataGeneratorTool"
-			/>
-			<Tool
-				Name="VCWebServiceProxyGeneratorTool"
-			/>
-			<Tool
-				Name="VCMIDLTool"
-			/>
-			<Tool
-				Name="VCCLCompilerTool"
-				PreprocessorDefinitions="_LIB;"
-				ExecutionBucket="7"
-				AdditionalIncludeDirectories="../include,../../pjlib/include"
-				PrecompiledHeaderFile=""
-			/>
-			<Tool
-				Name="VCManagedResourceCompilerTool"
-			/>
-			<Tool
-				Name="VCResourceCompilerTool"
-			/>
-			<Tool
-				Name="VCPreLinkEventTool"
-			/>
-			<Tool
-				Name="VCLibrarianTool"
-				OutputFile="..\lib\pjlib-util-$(TargetCPU)-wm6pro-vc$(VSVer)-$(ConfigurationName).lib"
-			/>
-			<Tool
-				Name="VCALinkTool"
-			/>
-			<Tool
-				Name="VCXDCMakeTool"
-			/>
-			<Tool
-				Name="VCBscMakeTool"
-			/>
-			<Tool
-				Name="VCCodeSignTool"
-			/>
-			<Tool
-				Name="VCPostBuildEventTool"
-			/>
-			<DeploymentTool
-				ForceDirty="-1"
-				RemoteDirectory=""
-				RegisterOutput="0"
-				AdditionalFiles=""
-			/>
-			<DebuggerTool
-			/>
-		</Configuration>
-		<Configuration
-			Name="Debug|Windows Mobile 6 Standard SDK (ARMV4I)"
-			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-common-defaults.vsprops"
-			ATLMinimizesCRunTimeLibraryUsage="false"
-			CharacterSet="1"
-			ConfigurationType="4"
-			>
-			<Tool
-				Name="VCPreBuildEventTool"
-			/>
-			<Tool
-				Name="VCCustomBuildTool"
-			/>
-			<Tool
-				Name="VCXMLDataGeneratorTool"
-			/>
-			<Tool
-				Name="VCWebServiceProxyGeneratorTool"
-			/>
-			<Tool
-				Name="VCMIDLTool"
-			/>
-			<Tool
-				Name="VCCLCompilerTool"
-				PreprocessorDefinitions="_LIB;"
-				ExecutionBucket="7"
-				AdditionalIncludeDirectories="../include,../../pjlib/include"
-				PrecompiledHeaderFile=""
-			/>
-			<Tool
-				Name="VCManagedResourceCompilerTool"
-			/>
-			<Tool
-				Name="VCResourceCompilerTool"
-			/>
-			<Tool
-				Name="VCPreLinkEventTool"
-			/>
-			<Tool
-				Name="VCLibrarianTool"
-				OutputFile="..\lib\pjlib-util-$(TargetCPU)-wm6std-vc$(VSVer)-$(ConfigurationName).lib"
-			/>
-			<Tool
-				Name="VCALinkTool"
-			/>
-			<Tool
-				Name="VCXDCMakeTool"
-			/>
-			<Tool
-				Name="VCBscMakeTool"
-			/>
-			<Tool
-				Name="VCCodeSignTool"
-			/>
-			<Tool
-				Name="VCPostBuildEventTool"
-			/>
-			<DeploymentTool
-				ForceDirty="-1"
-				RemoteDirectory=""
-				RegisterOutput="0"
-				AdditionalFiles=""
-			/>
-			<DebuggerTool
-			/>
-		</Configuration>
-		<Configuration
-			Name="Debug|Windows Mobile 6 Professional SDK (ARMV4I)"
-			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-common-defaults.vsprops"
-			ATLMinimizesCRunTimeLibraryUsage="false"
-			CharacterSet="1"
-			ConfigurationType="4"
-			>
-			<Tool
-				Name="VCPreBuildEventTool"
-			/>
-			<Tool
-				Name="VCCustomBuildTool"
-			/>
-			<Tool
-				Name="VCXMLDataGeneratorTool"
-			/>
-			<Tool
-				Name="VCWebServiceProxyGeneratorTool"
-			/>
-			<Tool
-				Name="VCMIDLTool"
-			/>
-			<Tool
-				Name="VCCLCompilerTool"
-				PreprocessorDefinitions="_LIB;"
-				ExecutionBucket="7"
-				AdditionalIncludeDirectories="../include,../../pjlib/include"
-				PrecompiledHeaderFile=""
-			/>
-			<Tool
-				Name="VCManagedResourceCompilerTool"
-			/>
-			<Tool
-				Name="VCResourceCompilerTool"
-			/>
-			<Tool
-				Name="VCPreLinkEventTool"
-			/>
-			<Tool
-				Name="VCLibrarianTool"
-				OutputFile="..\lib\pjlib-util-$(TargetCPU)-wm6pro-vc$(VSVer)-$(ConfigurationName).lib"
-			/>
-			<Tool
-				Name="VCALinkTool"
-			/>
-			<Tool
-				Name="VCXDCMakeTool"
-			/>
-			<Tool
-				Name="VCBscMakeTool"
-			/>
-			<Tool
-				Name="VCCodeSignTool"
-			/>
-			<Tool
-				Name="VCPostBuildEventTool"
-			/>
-			<DeploymentTool
-				ForceDirty="-1"
-				RemoteDirectory=""
-				RegisterOutput="0"
-				AdditionalFiles=""
-			/>
-			<DebuggerTool
-			/>
-		</Configuration>
-		<Configuration
-			Name="Debug-Static|Windows Mobile 6 Standard SDK (ARMV4I)"
-			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-common-defaults.vsprops"
-			ATLMinimizesCRunTimeLibraryUsage="false"
-			CharacterSet="1"
-			ConfigurationType="4"
-			>
-			<Tool
-				Name="VCPreBuildEventTool"
-			/>
-			<Tool
-				Name="VCCustomBuildTool"
-			/>
-			<Tool
-				Name="VCXMLDataGeneratorTool"
-			/>
-			<Tool
-				Name="VCWebServiceProxyGeneratorTool"
-			/>
-			<Tool
-				Name="VCMIDLTool"
-			/>
-			<Tool
-				Name="VCCLCompilerTool"
-				PreprocessorDefinitions="_LIB;"
-				ExecutionBucket="7"
-				AdditionalIncludeDirectories="../include,../../pjlib/include"
-				PrecompiledHeaderFile=""
-			/>
-			<Tool
-				Name="VCManagedResourceCompilerTool"
-			/>
-			<Tool
-				Name="VCResourceCompilerTool"
-			/>
-			<Tool
-				Name="VCPreLinkEventTool"
-			/>
-			<Tool
-				Name="VCLibrarianTool"
-				OutputFile="..\lib\pjlib-util-$(TargetCPU)-wm6std-vc$(VSVer)-$(ConfigurationName).lib"
-			/>
-			<Tool
-				Name="VCALinkTool"
-			/>
-			<Tool
-				Name="VCXDCMakeTool"
-			/>
-			<Tool
-				Name="VCBscMakeTool"
-			/>
-			<Tool
-				Name="VCCodeSignTool"
-			/>
-			<Tool
-				Name="VCPostBuildEventTool"
-			/>
-			<DeploymentTool
-				ForceDirty="-1"
-				RemoteDirectory=""
-				RegisterOutput="0"
-				AdditionalFiles=""
-			/>
-			<DebuggerTool
-			/>
-		</Configuration>
-		<Configuration
-			Name="Debug-Static|Windows Mobile 6 Professional SDK (ARMV4I)"
-			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-common-defaults.vsprops"
-			ATLMinimizesCRunTimeLibraryUsage="false"
-			CharacterSet="1"
-			ConfigurationType="4"
-			>
-			<Tool
-				Name="VCPreBuildEventTool"
-			/>
-			<Tool
-				Name="VCCustomBuildTool"
-			/>
-			<Tool
-				Name="VCXMLDataGeneratorTool"
-			/>
-			<Tool
-				Name="VCWebServiceProxyGeneratorTool"
-			/>
-			<Tool
-				Name="VCMIDLTool"
-			/>
-			<Tool
-				Name="VCCLCompilerTool"
-				PreprocessorDefinitions="_LIB;"
-				ExecutionBucket="7"
-				AdditionalIncludeDirectories="../include,../../pjlib/include"
-				PrecompiledHeaderFile=""
-			/>
-			<Tool
-				Name="VCManagedResourceCompilerTool"
-			/>
-			<Tool
-				Name="VCResourceCompilerTool"
-			/>
-			<Tool
-				Name="VCPreLinkEventTool"
-			/>
-			<Tool
-				Name="VCLibrarianTool"
-				OutputFile="..\lib\pjlib-util-$(TargetCPU)-wm6pro-vc$(VSVer)-$(ConfigurationName).lib"
-			/>
-			<Tool
-				Name="VCALinkTool"
-			/>
-			<Tool
-				Name="VCXDCMakeTool"
-			/>
-			<Tool
-				Name="VCBscMakeTool"
-			/>
-			<Tool
-				Name="VCCodeSignTool"
-			/>
-			<Tool
-				Name="VCPostBuildEventTool"
-			/>
-			<DeploymentTool
-				ForceDirty="-1"
-				RemoteDirectory=""
-				RegisterOutput="0"
-				AdditionalFiles=""
-			/>
-			<DebuggerTool
-			/>
-		</Configuration>
-		<Configuration
-			Name="Release-Dynamic|Windows Mobile 6 Standard SDK (ARMV4I)"
-			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-release-defaults.vsprops"
-			ATLMinimizesCRunTimeLibraryUsage="false"
-			CharacterSet="1"
-			ConfigurationType="4"
-			>
-			<Tool
-				Name="VCPreBuildEventTool"
-			/>
-			<Tool
-				Name="VCCustomBuildTool"
-			/>
-			<Tool
-				Name="VCXMLDataGeneratorTool"
-			/>
-			<Tool
-				Name="VCWebServiceProxyGeneratorTool"
-			/>
-			<Tool
-				Name="VCMIDLTool"
-			/>
-			<Tool
-				Name="VCCLCompilerTool"
-				PreprocessorDefinitions="_LIB;"
-				ExecutionBucket="7"
-				AdditionalIncludeDirectories="../include,../../pjlib/include"
-				PrecompiledHeaderFile=""
-			/>
-			<Tool
-				Name="VCManagedResourceCompilerTool"
-			/>
-			<Tool
-				Name="VCResourceCompilerTool"
-			/>
-			<Tool
-				Name="VCPreLinkEventTool"
-			/>
-			<Tool
-				Name="VCLibrarianTool"
-				OutputFile="..\lib\pjlib-util-$(TargetCPU)-wm6std-vc$(VSVer)-$(ConfigurationName).lib"
-			/>
-			<Tool
-				Name="VCALinkTool"
-			/>
-			<Tool
-				Name="VCXDCMakeTool"
-			/>
-			<Tool
-				Name="VCBscMakeTool"
-			/>
-			<Tool
-				Name="VCCodeSignTool"
-			/>
-			<Tool
-				Name="VCPostBuildEventTool"
-			/>
-			<DeploymentTool
-				ForceDirty="-1"
-				RemoteDirectory=""
-				RegisterOutput="0"
-				AdditionalFiles=""
-			/>
-			<DebuggerTool
-			/>
-		</Configuration>
-		<Configuration
-			Name="Release-Dynamic|Windows Mobile 6 Professional SDK (ARMV4I)"
-			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-release-defaults.vsprops"
-			ATLMinimizesCRunTimeLibraryUsage="false"
-			CharacterSet="1"
-			ConfigurationType="4"
-			>
-			<Tool
-				Name="VCPreBuildEventTool"
-			/>
-			<Tool
-				Name="VCCustomBuildTool"
-			/>
-			<Tool
-				Name="VCXMLDataGeneratorTool"
-			/>
-			<Tool
-				Name="VCWebServiceProxyGeneratorTool"
-			/>
-			<Tool
-				Name="VCMIDLTool"
-			/>
-			<Tool
-				Name="VCCLCompilerTool"
-				PreprocessorDefinitions="_LIB;"
-				ExecutionBucket="7"
-				AdditionalIncludeDirectories="../include,../../pjlib/include"
-				PrecompiledHeaderFile=""
-			/>
-			<Tool
-				Name="VCManagedResourceCompilerTool"
-			/>
-			<Tool
-				Name="VCResourceCompilerTool"
-			/>
-			<Tool
-				Name="VCPreLinkEventTool"
-			/>
-			<Tool
-				Name="VCLibrarianTool"
-				OutputFile="..\lib\pjlib-util-$(TargetCPU)-wm6pro-vc$(VSVer)-$(ConfigurationName).lib"
-			/>
-			<Tool
-				Name="VCALinkTool"
-			/>
-			<Tool
-				Name="VCXDCMakeTool"
-			/>
-			<Tool
-				Name="VCBscMakeTool"
-			/>
-			<Tool
-				Name="VCCodeSignTool"
-			/>
-			<Tool
-				Name="VCPostBuildEventTool"
-			/>
-			<DeploymentTool
-				ForceDirty="-1"
-				RemoteDirectory=""
-				RegisterOutput="0"
-				AdditionalFiles=""
-			/>
-			<DebuggerTool
-			/>
-		</Configuration>
-		<Configuration
-			Name="Debug-Dynamic|Windows Mobile 6 Standard SDK (ARMV4I)"
-			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-common-defaults.vsprops"
-			ATLMinimizesCRunTimeLibraryUsage="false"
-			CharacterSet="1"
-			ConfigurationType="4"
-			>
-			<Tool
-				Name="VCPreBuildEventTool"
-			/>
-			<Tool
-				Name="VCCustomBuildTool"
-			/>
-			<Tool
-				Name="VCXMLDataGeneratorTool"
-			/>
-			<Tool
-				Name="VCWebServiceProxyGeneratorTool"
-			/>
-			<Tool
-				Name="VCMIDLTool"
-			/>
-			<Tool
-				Name="VCCLCompilerTool"
-				PreprocessorDefinitions="_LIB;"
-				ExecutionBucket="7"
-				AdditionalIncludeDirectories="../include,../../pjlib/include"
-				PrecompiledHeaderFile=""
-			/>
-			<Tool
-				Name="VCManagedResourceCompilerTool"
-			/>
-			<Tool
-				Name="VCResourceCompilerTool"
-			/>
-			<Tool
-				Name="VCPreLinkEventTool"
-			/>
-			<Tool
-				Name="VCLibrarianTool"
-				OutputFile="..\lib\pjlib-util-$(TargetCPU)-wm6std-vc$(VSVer)-$(ConfigurationName).lib"
-			/>
-			<Tool
-				Name="VCALinkTool"
-			/>
-			<Tool
-				Name="VCXDCMakeTool"
-			/>
-			<Tool
-				Name="VCBscMakeTool"
-			/>
-			<Tool
-				Name="VCCodeSignTool"
-			/>
-			<Tool
-				Name="VCPostBuildEventTool"
-			/>
-			<DeploymentTool
-				ForceDirty="-1"
-				RemoteDirectory=""
-				RegisterOutput="0"
-				AdditionalFiles=""
-			/>
-			<DebuggerTool
-			/>
-		</Configuration>
-		<Configuration
-			Name="Debug-Dynamic|Windows Mobile 6 Professional SDK (ARMV4I)"
-			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-common-defaults.vsprops"
-			ATLMinimizesCRunTimeLibraryUsage="false"
-			CharacterSet="1"
-			ConfigurationType="4"
-			>
-			<Tool
-				Name="VCPreBuildEventTool"
-			/>
-			<Tool
-				Name="VCCustomBuildTool"
-			/>
-			<Tool
-				Name="VCXMLDataGeneratorTool"
-			/>
-			<Tool
-				Name="VCWebServiceProxyGeneratorTool"
-			/>
-			<Tool
-				Name="VCMIDLTool"
-			/>
-			<Tool
-				Name="VCCLCompilerTool"
-				PreprocessorDefinitions="_LIB;"
-				ExecutionBucket="7"
-				AdditionalIncludeDirectories="../include,../../pjlib/include"
-				PrecompiledHeaderFile=""
-			/>
-			<Tool
-				Name="VCManagedResourceCompilerTool"
-			/>
-			<Tool
-				Name="VCResourceCompilerTool"
-			/>
-			<Tool
-				Name="VCPreLinkEventTool"
-			/>
-			<Tool
-				Name="VCLibrarianTool"
-				OutputFile="..\lib\pjlib-util-$(TargetCPU)-wm6pro-vc$(VSVer)-$(ConfigurationName).lib"
-			/>
-			<Tool
-				Name="VCALinkTool"
-			/>
-			<Tool
-				Name="VCXDCMakeTool"
-			/>
-			<Tool
-				Name="VCBscMakeTool"
-			/>
-			<Tool
-				Name="VCCodeSignTool"
-			/>
-			<Tool
-				Name="VCPostBuildEventTool"
-			/>
-			<DeploymentTool
-				ForceDirty="-1"
-				RemoteDirectory=""
-				RegisterOutput="0"
-				AdditionalFiles=""
-			/>
-			<DebuggerTool
-			/>
-		</Configuration>
-		<Configuration
-			Name="Release-Static|Windows Mobile 6 Standard SDK (ARMV4I)"
 			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-release-defaults.vsprops"
 			ATLMinimizesCRunTimeLibraryUsage="false"
 			CharacterSet="1"
-			ConfigurationType="4"
 			>
 			<Tool
@@ -2217,73 +2283,7 @@
 			<Tool
 				Name="VCCLCompilerTool"
-				PreprocessorDefinitions="_LIB;"
 				ExecutionBucket="7"
 				AdditionalIncludeDirectories="../include,../../pjlib/include"
-				PrecompiledHeaderFile=""
-			/>
-			<Tool
-				Name="VCManagedResourceCompilerTool"
-			/>
-			<Tool
-				Name="VCResourceCompilerTool"
-			/>
-			<Tool
-				Name="VCPreLinkEventTool"
-			/>
-			<Tool
-				Name="VCLibrarianTool"
-				OutputFile="..\lib\pjlib-util-$(TargetCPU)-wm6std-vc$(VSVer)-$(ConfigurationName).lib"
-			/>
-			<Tool
-				Name="VCALinkTool"
-			/>
-			<Tool
-				Name="VCXDCMakeTool"
-			/>
-			<Tool
-				Name="VCBscMakeTool"
-			/>
-			<Tool
-				Name="VCCodeSignTool"
-			/>
-			<Tool
-				Name="VCPostBuildEventTool"
-			/>
-			<DeploymentTool
-				ForceDirty="-1"
-				RemoteDirectory=""
-				RegisterOutput="0"
-				AdditionalFiles=""
-			/>
-			<DebuggerTool
-			/>
-		</Configuration>
-		<Configuration
-			Name="Release-Static|Windows Mobile 6 Professional SDK (ARMV4I)"
-			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-release-defaults.vsprops"
-			ATLMinimizesCRunTimeLibraryUsage="false"
-			CharacterSet="1"
-			ConfigurationType="4"
-			>
-			<Tool
-				Name="VCPreBuildEventTool"
-			/>
-			<Tool
-				Name="VCCustomBuildTool"
-			/>
-			<Tool
-				Name="VCXMLDataGeneratorTool"
-			/>
-			<Tool
-				Name="VCWebServiceProxyGeneratorTool"
-			/>
-			<Tool
-				Name="VCMIDLTool"
-			/>
-			<Tool
-				Name="VCCLCompilerTool"
 				PreprocessorDefinitions="_LIB;"
-				ExecutionBucket="7"
-				AdditionalIncludeDirectories="../include,../../pjlib/include"
 				PrecompiledHeaderFile=""
 			/>
@@ -3934,4 +3934,8 @@
 			</File>
 			<File
+				RelativePath="..\src\pjlib-util\json.c"
+				>
+			</File>
+			<File
 				RelativePath="..\src\pjlib-util\md5.c"
 				>
@@ -4313,4 +4317,20 @@
 				</FileConfiguration>
 				<FileConfiguration
+					Name="Release|Windows Mobile 6 Standard SDK (ARMV4I)"
+					ExcludedFromBuild="true"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Release|Windows Mobile 6 Professional SDK (ARMV4I)"
+					ExcludedFromBuild="true"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+					/>
+				</FileConfiguration>
+				<FileConfiguration
 					Name="Debug|Win32"
 					ExcludedFromBuild="true"
@@ -4349,4 +4369,20 @@
 				</FileConfiguration>
 				<FileConfiguration
+					Name="Debug|Windows Mobile 6 Standard SDK (ARMV4I)"
+					ExcludedFromBuild="true"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Debug|Windows Mobile 6 Professional SDK (ARMV4I)"
+					ExcludedFromBuild="true"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+					/>
+				</FileConfiguration>
+				<FileConfiguration
 					Name="Debug-Static|Win32"
 					ExcludedFromBuild="true"
@@ -4385,4 +4421,20 @@
 				</FileConfiguration>
 				<FileConfiguration
+					Name="Debug-Static|Windows Mobile 6 Standard SDK (ARMV4I)"
+					ExcludedFromBuild="true"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Debug-Static|Windows Mobile 6 Professional SDK (ARMV4I)"
+					ExcludedFromBuild="true"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+					/>
+				</FileConfiguration>
+				<FileConfiguration
 					Name="Release-Dynamic|Win32"
 					ExcludedFromBuild="true"
@@ -4421,4 +4473,20 @@
 				</FileConfiguration>
 				<FileConfiguration
+					Name="Release-Dynamic|Windows Mobile 6 Standard SDK (ARMV4I)"
+					ExcludedFromBuild="true"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Release-Dynamic|Windows Mobile 6 Professional SDK (ARMV4I)"
+					ExcludedFromBuild="true"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+					/>
+				</FileConfiguration>
+				<FileConfiguration
 					Name="Debug-Dynamic|Win32"
 					ExcludedFromBuild="true"
@@ -4457,4 +4525,20 @@
 				</FileConfiguration>
 				<FileConfiguration
+					Name="Debug-Dynamic|Windows Mobile 6 Standard SDK (ARMV4I)"
+					ExcludedFromBuild="true"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Debug-Dynamic|Windows Mobile 6 Professional SDK (ARMV4I)"
+					ExcludedFromBuild="true"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+					/>
+				</FileConfiguration>
+				<FileConfiguration
 					Name="Release-Static|Win32"
 					ExcludedFromBuild="true"
@@ -4490,84 +4574,4 @@
 						AdditionalIncludeDirectories=""
 						PreprocessorDefinitions=""
-					/>
-				</FileConfiguration>
-				<FileConfiguration
-					Name="Release|Windows Mobile 6 Standard SDK (ARMV4I)"
-					ExcludedFromBuild="true"
-					>
-					<Tool
-						Name="VCCLCompilerTool"
-					/>
-				</FileConfiguration>
-				<FileConfiguration
-					Name="Release|Windows Mobile 6 Professional SDK (ARMV4I)"
-					ExcludedFromBuild="true"
-					>
-					<Tool
-						Name="VCCLCompilerTool"
-					/>
-				</FileConfiguration>
-				<FileConfiguration
-					Name="Debug|Windows Mobile 6 Standard SDK (ARMV4I)"
-					ExcludedFromBuild="true"
-					>
-					<Tool
-						Name="VCCLCompilerTool"
-					/>
-				</FileConfiguration>
-				<FileConfiguration
-					Name="Debug|Windows Mobile 6 Professional SDK (ARMV4I)"
-					ExcludedFromBuild="true"
-					>
-					<Tool
-						Name="VCCLCompilerTool"
-					/>
-				</FileConfiguration>
-				<FileConfiguration
-					Name="Debug-Static|Windows Mobile 6 Standard SDK (ARMV4I)"
-					ExcludedFromBuild="true"
-					>
-					<Tool
-						Name="VCCLCompilerTool"
-					/>
-				</FileConfiguration>
-				<FileConfiguration
-					Name="Debug-Static|Windows Mobile 6 Professional SDK (ARMV4I)"
-					ExcludedFromBuild="true"
-					>
-					<Tool
-						Name="VCCLCompilerTool"
-					/>
-				</FileConfiguration>
-				<FileConfiguration
-					Name="Release-Dynamic|Windows Mobile 6 Standard SDK (ARMV4I)"
-					ExcludedFromBuild="true"
-					>
-					<Tool
-						Name="VCCLCompilerTool"
-					/>
-				</FileConfiguration>
-				<FileConfiguration
-					Name="Release-Dynamic|Windows Mobile 6 Professional SDK (ARMV4I)"
-					ExcludedFromBuild="true"
-					>
-					<Tool
-						Name="VCCLCompilerTool"
-					/>
-				</FileConfiguration>
-				<FileConfiguration
-					Name="Debug-Dynamic|Windows Mobile 6 Standard SDK (ARMV4I)"
-					ExcludedFromBuild="true"
-					>
-					<Tool
-						Name="VCCLCompilerTool"
-					/>
-				</FileConfiguration>
-				<FileConfiguration
-					Name="Debug-Dynamic|Windows Mobile 6 Professional SDK (ARMV4I)"
-					ExcludedFromBuild="true"
-					>
-					<Tool
-						Name="VCCLCompilerTool"
 					/>
 				</FileConfiguration>
@@ -4725,4 +4729,20 @@
 				</FileConfiguration>
 				<FileConfiguration
+					Name="Release|Windows Mobile 6 Standard SDK (ARMV4I)"
+					ExcludedFromBuild="true"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Release|Windows Mobile 6 Professional SDK (ARMV4I)"
+					ExcludedFromBuild="true"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+					/>
+				</FileConfiguration>
+				<FileConfiguration
 					Name="Debug|Win32"
 					ExcludedFromBuild="true"
@@ -4761,4 +4781,20 @@
 				</FileConfiguration>
 				<FileConfiguration
+					Name="Debug|Windows Mobile 6 Standard SDK (ARMV4I)"
+					ExcludedFromBuild="true"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Debug|Windows Mobile 6 Professional SDK (ARMV4I)"
+					ExcludedFromBuild="true"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+					/>
+				</FileConfiguration>
+				<FileConfiguration
 					Name="Debug-Static|Win32"
 					ExcludedFromBuild="true"
@@ -4797,4 +4833,20 @@
 				</FileConfiguration>
 				<FileConfiguration
+					Name="Debug-Static|Windows Mobile 6 Standard SDK (ARMV4I)"
+					ExcludedFromBuild="true"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Debug-Static|Windows Mobile 6 Professional SDK (ARMV4I)"
+					ExcludedFromBuild="true"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+					/>
+				</FileConfiguration>
+				<FileConfiguration
 					Name="Release-Dynamic|Win32"
 					ExcludedFromBuild="true"
@@ -4833,4 +4885,20 @@
 				</FileConfiguration>
 				<FileConfiguration
+					Name="Release-Dynamic|Windows Mobile 6 Standard SDK (ARMV4I)"
+					ExcludedFromBuild="true"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Release-Dynamic|Windows Mobile 6 Professional SDK (ARMV4I)"
+					ExcludedFromBuild="true"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+					/>
+				</FileConfiguration>
+				<FileConfiguration
 					Name="Debug-Dynamic|Win32"
 					ExcludedFromBuild="true"
@@ -4869,4 +4937,20 @@
 				</FileConfiguration>
 				<FileConfiguration
+					Name="Debug-Dynamic|Windows Mobile 6 Standard SDK (ARMV4I)"
+					ExcludedFromBuild="true"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Debug-Dynamic|Windows Mobile 6 Professional SDK (ARMV4I)"
+					ExcludedFromBuild="true"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+					/>
+				</FileConfiguration>
+				<FileConfiguration
 					Name="Release-Static|Win32"
 					ExcludedFromBuild="true"
@@ -4902,84 +4986,4 @@
 						AdditionalIncludeDirectories=""
 						PreprocessorDefinitions=""
-					/>
-				</FileConfiguration>
-				<FileConfiguration
-					Name="Release|Windows Mobile 6 Standard SDK (ARMV4I)"
-					ExcludedFromBuild="true"
-					>
-					<Tool
-						Name="VCCLCompilerTool"
-					/>
-				</FileConfiguration>
-				<FileConfiguration
-					Name="Release|Windows Mobile 6 Professional SDK (ARMV4I)"
-					ExcludedFromBuild="true"
-					>
-					<Tool
-						Name="VCCLCompilerTool"
-					/>
-				</FileConfiguration>
-				<FileConfiguration
-					Name="Debug|Windows Mobile 6 Standard SDK (ARMV4I)"
-					ExcludedFromBuild="true"
-					>
-					<Tool
-						Name="VCCLCompilerTool"
-					/>
-				</FileConfiguration>
-				<FileConfiguration
-					Name="Debug|Windows Mobile 6 Professional SDK (ARMV4I)"
-					ExcludedFromBuild="true"
-					>
-					<Tool
-						Name="VCCLCompilerTool"
-					/>
-				</FileConfiguration>
-				<FileConfiguration
-					Name="Debug-Static|Windows Mobile 6 Standard SDK (ARMV4I)"
-					ExcludedFromBuild="true"
-					>
-					<Tool
-						Name="VCCLCompilerTool"
-					/>
-				</FileConfiguration>
-				<FileConfiguration
-					Name="Debug-Static|Windows Mobile 6 Professional SDK (ARMV4I)"
-					ExcludedFromBuild="true"
-					>
-					<Tool
-						Name="VCCLCompilerTool"
-					/>
-				</FileConfiguration>
-				<FileConfiguration
-					Name="Release-Dynamic|Windows Mobile 6 Standard SDK (ARMV4I)"
-					ExcludedFromBuild="true"
-					>
-					<Tool
-						Name="VCCLCompilerTool"
-					/>
-				</FileConfiguration>
-				<FileConfiguration
-					Name="Release-Dynamic|Windows Mobile 6 Professional SDK (ARMV4I)"
-					ExcludedFromBuild="true"
-					>
-					<Tool
-						Name="VCCLCompilerTool"
-					/>
-				</FileConfiguration>
-				<FileConfiguration
-					Name="Debug-Dynamic|Windows Mobile 6 Standard SDK (ARMV4I)"
-					ExcludedFromBuild="true"
-					>
-					<Tool
-						Name="VCCLCompilerTool"
-					/>
-				</FileConfiguration>
-				<FileConfiguration
-					Name="Debug-Dynamic|Windows Mobile 6 Professional SDK (ARMV4I)"
-					ExcludedFromBuild="true"
-					>
-					<Tool
-						Name="VCCLCompilerTool"
 					/>
 				</FileConfiguration>
@@ -5587,4 +5591,20 @@
 				</FileConfiguration>
 				<FileConfiguration
+					Name="Release|Windows Mobile 6 Standard SDK (ARMV4I)"
+					ExcludedFromBuild="true"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Release|Windows Mobile 6 Professional SDK (ARMV4I)"
+					ExcludedFromBuild="true"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+					/>
+				</FileConfiguration>
+				<FileConfiguration
 					Name="Debug|Win32"
 					>
@@ -5621,4 +5641,20 @@
 				</FileConfiguration>
 				<FileConfiguration
+					Name="Debug|Windows Mobile 6 Standard SDK (ARMV4I)"
+					ExcludedFromBuild="true"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Debug|Windows Mobile 6 Professional SDK (ARMV4I)"
+					ExcludedFromBuild="true"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+					/>
+				</FileConfiguration>
+				<FileConfiguration
 					Name="Debug-Static|Win32"
 					>
@@ -5655,4 +5691,20 @@
 				</FileConfiguration>
 				<FileConfiguration
+					Name="Debug-Static|Windows Mobile 6 Standard SDK (ARMV4I)"
+					ExcludedFromBuild="true"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Debug-Static|Windows Mobile 6 Professional SDK (ARMV4I)"
+					ExcludedFromBuild="true"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+					/>
+				</FileConfiguration>
+				<FileConfiguration
 					Name="Release-Dynamic|Win32"
 					>
@@ -5689,4 +5741,20 @@
 				</FileConfiguration>
 				<FileConfiguration
+					Name="Release-Dynamic|Windows Mobile 6 Standard SDK (ARMV4I)"
+					ExcludedFromBuild="true"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Release-Dynamic|Windows Mobile 6 Professional SDK (ARMV4I)"
+					ExcludedFromBuild="true"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+					/>
+				</FileConfiguration>
+				<FileConfiguration
 					Name="Debug-Dynamic|Win32"
 					>
@@ -5723,4 +5791,20 @@
 				</FileConfiguration>
 				<FileConfiguration
+					Name="Debug-Dynamic|Windows Mobile 6 Standard SDK (ARMV4I)"
+					ExcludedFromBuild="true"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Debug-Dynamic|Windows Mobile 6 Professional SDK (ARMV4I)"
+					ExcludedFromBuild="true"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+					/>
+				</FileConfiguration>
+				<FileConfiguration
 					Name="Release-Static|Win32"
 					>
@@ -5754,84 +5838,4 @@
 						AdditionalIncludeDirectories=""
 						PreprocessorDefinitions=""
-					/>
-				</FileConfiguration>
-				<FileConfiguration
-					Name="Release|Windows Mobile 6 Standard SDK (ARMV4I)"
-					ExcludedFromBuild="true"
-					>
-					<Tool
-						Name="VCCLCompilerTool"
-					/>
-				</FileConfiguration>
-				<FileConfiguration
-					Name="Release|Windows Mobile 6 Professional SDK (ARMV4I)"
-					ExcludedFromBuild="true"
-					>
-					<Tool
-						Name="VCCLCompilerTool"
-					/>
-				</FileConfiguration>
-				<FileConfiguration
-					Name="Debug|Windows Mobile 6 Standard SDK (ARMV4I)"
-					ExcludedFromBuild="true"
-					>
-					<Tool
-						Name="VCCLCompilerTool"
-					/>
-				</FileConfiguration>
-				<FileConfiguration
-					Name="Debug|Windows Mobile 6 Professional SDK (ARMV4I)"
-					ExcludedFromBuild="true"
-					>
-					<Tool
-						Name="VCCLCompilerTool"
-					/>
-				</FileConfiguration>
-				<FileConfiguration
-					Name="Debug-Static|Windows Mobile 6 Standard SDK (ARMV4I)"
-					ExcludedFromBuild="true"
-					>
-					<Tool
-						Name="VCCLCompilerTool"
-					/>
-				</FileConfiguration>
-				<FileConfiguration
-					Name="Debug-Static|Windows Mobile 6 Professional SDK (ARMV4I)"
-					ExcludedFromBuild="true"
-					>
-					<Tool
-						Name="VCCLCompilerTool"
-					/>
-				</FileConfiguration>
-				<FileConfiguration
-					Name="Release-Dynamic|Windows Mobile 6 Standard SDK (ARMV4I)"
-					ExcludedFromBuild="true"
-					>
-					<Tool
-						Name="VCCLCompilerTool"
-					/>
-				</FileConfiguration>
-				<FileConfiguration
-					Name="Release-Dynamic|Windows Mobile 6 Professional SDK (ARMV4I)"
-					ExcludedFromBuild="true"
-					>
-					<Tool
-						Name="VCCLCompilerTool"
-					/>
-				</FileConfiguration>
-				<FileConfiguration
-					Name="Debug-Dynamic|Windows Mobile 6 Standard SDK (ARMV4I)"
-					ExcludedFromBuild="true"
-					>
-					<Tool
-						Name="VCCLCompilerTool"
-					/>
-				</FileConfiguration>
-				<FileConfiguration
-					Name="Debug-Dynamic|Windows Mobile 6 Professional SDK (ARMV4I)"
-					ExcludedFromBuild="true"
-					>
-					<Tool
-						Name="VCCLCompilerTool"
 					/>
 				</FileConfiguration>
@@ -6123,4 +6127,8 @@
 			</File>
 			<File
+				RelativePath="..\include\pjlib-util\json.h"
+				>
+			</File>
+			<File
 				RelativePath="..\include\pjlib-util\md5.h"
 				>
Index: /pjproject/trunk/pjlib-util/build/pjlib_util_test.vcproj
===================================================================
--- /pjproject/trunk/pjlib-util/build/pjlib_util_test.vcproj	(revision 4703)
+++ /pjproject/trunk/pjlib-util/build/pjlib_util_test.vcproj	(revision 4704)
@@ -307,75 +307,7 @@
 		</Configuration>
 		<Configuration
-			Name="Debug|Win32"
-			ConfigurationType="1"
-			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-win32-common-defaults.vsprops"
-			UseOfMFC="0"
-			ATLMinimizesCRunTimeLibraryUsage="false"
-			CharacterSet="2"
-			>
-			<Tool
-				Name="VCPreBuildEventTool"
-			/>
-			<Tool
-				Name="VCCustomBuildTool"
-			/>
-			<Tool
-				Name="VCXMLDataGeneratorTool"
-			/>
-			<Tool
-				Name="VCWebServiceProxyGeneratorTool"
-			/>
-			<Tool
-				Name="VCMIDLTool"
-			/>
-			<Tool
-				Name="VCCLCompilerTool"
-				AdditionalIncludeDirectories="../include,../../pjlib/include"
-				PreprocessorDefinitions="_CONSOLE;"
-				PrecompiledHeaderFile=""
-			/>
-			<Tool
-				Name="VCManagedResourceCompilerTool"
-			/>
-			<Tool
-				Name="VCResourceCompilerTool"
-			/>
-			<Tool
-				Name="VCPreLinkEventTool"
-			/>
-			<Tool
-				Name="VCLinkerTool"
-				AdditionalDependencies="netapi32.lib mswsock.lib ws2_32.lib odbc32.lib odbccp32.lib oleaut32.lib ole32.lib"
-				OutputFile="..\bin\pjlib-util-test-$(TargetCPU)-$(PlatformName)-vc$(VSVer)-$(ConfigurationName).exe"
-			/>
-			<Tool
-				Name="VCALinkTool"
-			/>
-			<Tool
-				Name="VCManifestTool"
-			/>
-			<Tool
-				Name="VCXDCMakeTool"
-			/>
-			<Tool
-				Name="VCBscMakeTool"
-			/>
-			<Tool
-				Name="VCFxCopTool"
-			/>
-			<Tool
-				Name="VCAppVerifierTool"
-			/>
-			<Tool
-				Name="VCWebDeploymentTool"
-			/>
-			<Tool
-				Name="VCPostBuildEventTool"
-			/>
-		</Configuration>
-		<Configuration
-			Name="Debug|Pocket PC 2003 (ARMV4)"
-			ConfigurationType="1"
-			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-common-defaults.vsprops"
+			Name="Release|Windows Mobile 6 Standard SDK (ARMV4I)"
+			ConfigurationType="1"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-release-defaults.vsprops"
 			ATLMinimizesCRunTimeLibraryUsage="false"
 			CharacterSet="1"
@@ -414,5 +346,5 @@
 				Name="VCLinkerTool"
 				AdditionalDependencies="ws2.lib"
-				OutputFile="..\bin\pjlib-util-test-$(TargetCPU)-wm2003ppc-vc$(VSVer)-$(ConfigurationName).exe"
+				OutputFile="..\bin\pjlib-util-test-$(TargetCPU)-wm6std-vc$(VSVer)-$(ConfigurationName).exe"
 			/>
 			<Tool
@@ -441,7 +373,7 @@
 		</Configuration>
 		<Configuration
-			Name="Debug|Smartphone 2003 (ARMV4)"
-			ConfigurationType="1"
-			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-common-defaults.vsprops"
+			Name="Release|Windows Mobile 6 Professional SDK (ARMV4I)"
+			ConfigurationType="1"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-release-defaults.vsprops"
 			ATLMinimizesCRunTimeLibraryUsage="false"
 			CharacterSet="1"
@@ -480,5 +412,5 @@
 				Name="VCLinkerTool"
 				AdditionalDependencies="ws2.lib"
-				OutputFile="..\bin\pjlib-util-test-$(TargetCPU)-wm2003sp-vc$(VSVer)-$(ConfigurationName).exe"
+				OutputFile="..\bin\pjlib-util-test-$(TargetCPU)-wm6pro-vc$(VSVer)-$(ConfigurationName).exe"
 			/>
 			<Tool
@@ -507,7 +439,7 @@
 		</Configuration>
 		<Configuration
-			Name="Debug|x64"
-			ConfigurationType="1"
-			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-win64-common-defaults.vsprops"
+			Name="Debug|Win32"
+			ConfigurationType="1"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-win32-common-defaults.vsprops"
 			UseOfMFC="0"
 			ATLMinimizesCRunTimeLibraryUsage="false"
@@ -528,5 +460,4 @@
 			<Tool
 				Name="VCMIDLTool"
-				TargetEnvironment="3"
 			/>
 			<Tool
@@ -535,5 +466,4 @@
 				PreprocessorDefinitions="_CONSOLE;"
 				PrecompiledHeaderFile=""
-				DebugInformationFormat="3"
 			/>
 			<Tool
@@ -550,5 +480,4 @@
 				AdditionalDependencies="netapi32.lib mswsock.lib ws2_32.lib odbc32.lib odbccp32.lib oleaut32.lib ole32.lib"
 				OutputFile="..\bin\pjlib-util-test-$(TargetCPU)-$(PlatformName)-vc$(VSVer)-$(ConfigurationName).exe"
-				TargetMachine="17"
 			/>
 			<Tool
@@ -578,73 +507,5 @@
 		</Configuration>
 		<Configuration
-			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"
-			ATLMinimizesCRunTimeLibraryUsage="false"
-			CharacterSet="2"
-			>
-			<Tool
-				Name="VCPreBuildEventTool"
-			/>
-			<Tool
-				Name="VCCustomBuildTool"
-			/>
-			<Tool
-				Name="VCXMLDataGeneratorTool"
-			/>
-			<Tool
-				Name="VCWebServiceProxyGeneratorTool"
-			/>
-			<Tool
-				Name="VCMIDLTool"
-			/>
-			<Tool
-				Name="VCCLCompilerTool"
-				AdditionalIncludeDirectories="../include,../../pjlib/include"
-				PreprocessorDefinitions="_CONSOLE;"
-				PrecompiledHeaderFile=""
-			/>
-			<Tool
-				Name="VCManagedResourceCompilerTool"
-			/>
-			<Tool
-				Name="VCResourceCompilerTool"
-			/>
-			<Tool
-				Name="VCPreLinkEventTool"
-			/>
-			<Tool
-				Name="VCLinkerTool"
-				AdditionalDependencies="netapi32.lib mswsock.lib ws2_32.lib odbc32.lib odbccp32.lib oleaut32.lib ole32.lib"
-				OutputFile="..\bin\pjlib-util-test-$(TargetCPU)-$(PlatformName)-vc$(VSVer)-$(ConfigurationName).exe"
-			/>
-			<Tool
-				Name="VCALinkTool"
-			/>
-			<Tool
-				Name="VCManifestTool"
-			/>
-			<Tool
-				Name="VCXDCMakeTool"
-			/>
-			<Tool
-				Name="VCBscMakeTool"
-			/>
-			<Tool
-				Name="VCFxCopTool"
-			/>
-			<Tool
-				Name="VCAppVerifierTool"
-			/>
-			<Tool
-				Name="VCWebDeploymentTool"
-			/>
-			<Tool
-				Name="VCPostBuildEventTool"
-			/>
-		</Configuration>
-		<Configuration
-			Name="Debug-Static|Pocket PC 2003 (ARMV4)"
+			Name="Debug|Pocket PC 2003 (ARMV4)"
 			ConfigurationType="1"
 			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-common-defaults.vsprops"
@@ -712,5 +573,5 @@
 		</Configuration>
 		<Configuration
-			Name="Debug-Static|Smartphone 2003 (ARMV4)"
+			Name="Debug|Smartphone 2003 (ARMV4)"
 			ConfigurationType="1"
 			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-common-defaults.vsprops"
@@ -778,5 +639,5 @@
 		</Configuration>
 		<Configuration
-			Name="Debug-Static|x64"
+			Name="Debug|x64"
 			ConfigurationType="1"
 			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-win64-common-defaults.vsprops"
@@ -849,75 +710,7 @@
 		</Configuration>
 		<Configuration
-			Name="Release-Dynamic|Win32"
-			ConfigurationType="1"
-			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-win32-release-defaults.vsprops"
-			UseOfMFC="0"
-			ATLMinimizesCRunTimeLibraryUsage="false"
-			CharacterSet="2"
-			>
-			<Tool
-				Name="VCPreBuildEventTool"
-			/>
-			<Tool
-				Name="VCCustomBuildTool"
-			/>
-			<Tool
-				Name="VCXMLDataGeneratorTool"
-			/>
-			<Tool
-				Name="VCWebServiceProxyGeneratorTool"
-			/>
-			<Tool
-				Name="VCMIDLTool"
-			/>
-			<Tool
-				Name="VCCLCompilerTool"
-				AdditionalIncludeDirectories="../include,../../pjlib/include"
-				PreprocessorDefinitions="_CONSOLE;"
-				PrecompiledHeaderFile=""
-			/>
-			<Tool
-				Name="VCManagedResourceCompilerTool"
-			/>
-			<Tool
-				Name="VCResourceCompilerTool"
-			/>
-			<Tool
-				Name="VCPreLinkEventTool"
-			/>
-			<Tool
-				Name="VCLinkerTool"
-				AdditionalDependencies="netapi32.lib mswsock.lib ws2_32.lib odbc32.lib odbccp32.lib oleaut32.lib ole32.lib"
-				OutputFile="..\bin\pjlib-util-test-$(TargetCPU)-$(PlatformName)-vc$(VSVer)-$(ConfigurationName).exe"
-			/>
-			<Tool
-				Name="VCALinkTool"
-			/>
-			<Tool
-				Name="VCManifestTool"
-			/>
-			<Tool
-				Name="VCXDCMakeTool"
-			/>
-			<Tool
-				Name="VCBscMakeTool"
-			/>
-			<Tool
-				Name="VCFxCopTool"
-			/>
-			<Tool
-				Name="VCAppVerifierTool"
-			/>
-			<Tool
-				Name="VCWebDeploymentTool"
-			/>
-			<Tool
-				Name="VCPostBuildEventTool"
-			/>
-		</Configuration>
-		<Configuration
-			Name="Release-Dynamic|Pocket PC 2003 (ARMV4)"
-			ConfigurationType="1"
-			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-release-defaults.vsprops"
+			Name="Debug|Windows Mobile 6 Standard SDK (ARMV4I)"
+			ConfigurationType="1"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-common-defaults.vsprops"
 			ATLMinimizesCRunTimeLibraryUsage="false"
 			CharacterSet="1"
@@ -956,5 +749,5 @@
 				Name="VCLinkerTool"
 				AdditionalDependencies="ws2.lib"
-				OutputFile="..\bin\pjlib-util-test-$(TargetCPU)-wm2003ppc-vc$(VSVer)-$(ConfigurationName).exe"
+				OutputFile="..\bin\pjlib-util-test-$(TargetCPU)-wm6std-vc$(VSVer)-$(ConfigurationName).exe"
 			/>
 			<Tool
@@ -983,7 +776,7 @@
 		</Configuration>
 		<Configuration
-			Name="Release-Dynamic|Smartphone 2003 (ARMV4)"
-			ConfigurationType="1"
-			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-release-defaults.vsprops"
+			Name="Debug|Windows Mobile 6 Professional SDK (ARMV4I)"
+			ConfigurationType="1"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-common-defaults.vsprops"
 			ATLMinimizesCRunTimeLibraryUsage="false"
 			CharacterSet="1"
@@ -1022,5 +815,5 @@
 				Name="VCLinkerTool"
 				AdditionalDependencies="ws2.lib"
-				OutputFile="..\bin\pjlib-util-test-$(TargetCPU)-wm2003sp-vc$(VSVer)-$(ConfigurationName).exe"
+				OutputFile="..\bin\pjlib-util-test-$(TargetCPU)-wm6pro-vc$(VSVer)-$(ConfigurationName).exe"
 			/>
 			<Tool
@@ -1049,7 +842,7 @@
 		</Configuration>
 		<Configuration
-			Name="Release-Dynamic|x64"
-			ConfigurationType="1"
-			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-win64-release-defaults.vsprops"
+			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"
 			ATLMinimizesCRunTimeLibraryUsage="false"
@@ -1070,5 +863,4 @@
 			<Tool
 				Name="VCMIDLTool"
-				TargetEnvironment="3"
 			/>
 			<Tool
@@ -1091,5 +883,4 @@
 				AdditionalDependencies="netapi32.lib mswsock.lib ws2_32.lib odbc32.lib odbccp32.lib oleaut32.lib ole32.lib"
 				OutputFile="..\bin\pjlib-util-test-$(TargetCPU)-$(PlatformName)-vc$(VSVer)-$(ConfigurationName).exe"
-				TargetMachine="17"
 			/>
 			<Tool
@@ -1119,75 +910,7 @@
 		</Configuration>
 		<Configuration
-			Name="Debug-Dynamic|Win32"
-			ConfigurationType="1"
-			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-win32-common-defaults.vsprops"
-			UseOfMFC="0"
-			ATLMinimizesCRunTimeLibraryUsage="false"
-			CharacterSet="2"
-			>
-			<Tool
-				Name="VCPreBuildEventTool"
-			/>
-			<Tool
-				Name="VCCustomBuildTool"
-			/>
-			<Tool
-				Name="VCXMLDataGeneratorTool"
-			/>
-			<Tool
-				Name="VCWebServiceProxyGeneratorTool"
-			/>
-			<Tool
-				Name="VCMIDLTool"
-			/>
-			<Tool
-				Name="VCCLCompilerTool"
-				AdditionalIncludeDirectories="../include,../../pjlib/include"
-				PreprocessorDefinitions="_CONSOLE;"
-				PrecompiledHeaderFile=""
-			/>
-			<Tool
-				Name="VCManagedResourceCompilerTool"
-			/>
-			<Tool
-				Name="VCResourceCompilerTool"
-			/>
-			<Tool
-				Name="VCPreLinkEventTool"
-			/>
-			<Tool
-				Name="VCLinkerTool"
-				AdditionalDependencies="netapi32.lib mswsock.lib ws2_32.lib odbc32.lib odbccp32.lib oleaut32.lib ole32.lib"
-				OutputFile="..\bin\pjlib-util-test-$(TargetCPU)-$(PlatformName)-vc$(VSVer)-$(ConfigurationName).exe"
-			/>
-			<Tool
-				Name="VCALinkTool"
-			/>
-			<Tool
-				Name="VCManifestTool"
-			/>
-			<Tool
-				Name="VCXDCMakeTool"
-			/>
-			<Tool
-				Name="VCBscMakeTool"
-			/>
-			<Tool
-				Name="VCFxCopTool"
-			/>
-			<Tool
-				Name="VCAppVerifierTool"
-			/>
-			<Tool
-				Name="VCWebDeploymentTool"
-			/>
-			<Tool
-				Name="VCPostBuildEventTool"
-			/>
-		</Configuration>
-		<Configuration
-			Name="Debug-Dynamic|Pocket PC 2003 (ARMV4)"
-			ConfigurationType="1"
-			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-common-defaults.vsprops"
+			Name="Debug-Static|Pocket PC 2003 (ARMV4)"
+			ConfigurationType="1"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-common-defaults.vsprops"
 			ATLMinimizesCRunTimeLibraryUsage="false"
 			CharacterSet="1"
@@ -1253,7 +976,7 @@
 		</Configuration>
 		<Configuration
-			Name="Debug-Dynamic|Smartphone 2003 (ARMV4)"
-			ConfigurationType="1"
-			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-common-defaults.vsprops"
+			Name="Debug-Static|Smartphone 2003 (ARMV4)"
+			ConfigurationType="1"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-common-defaults.vsprops"
 			ATLMinimizesCRunTimeLibraryUsage="false"
 			CharacterSet="1"
@@ -1319,7 +1042,7 @@
 		</Configuration>
 		<Configuration
-			Name="Debug-Dynamic|x64"
-			ConfigurationType="1"
-			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-win64-common-defaults.vsprops"
+			Name="Debug-Static|x64"
+			ConfigurationType="1"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-win64-common-defaults.vsprops"
 			UseOfMFC="0"
 			ATLMinimizesCRunTimeLibraryUsage="false"
@@ -1390,75 +1113,7 @@
 		</Configuration>
 		<Configuration
-			Name="Release-Static|Win32"
-			ConfigurationType="1"
-			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-win32-release-defaults.vsprops"
-			UseOfMFC="0"
-			ATLMinimizesCRunTimeLibraryUsage="false"
-			CharacterSet="2"
-			>
-			<Tool
-				Name="VCPreBuildEventTool"
-			/>
-			<Tool
-				Name="VCCustomBuildTool"
-			/>
-			<Tool
-				Name="VCXMLDataGeneratorTool"
-			/>
-			<Tool
-				Name="VCWebServiceProxyGeneratorTool"
-			/>
-			<Tool
-				Name="VCMIDLTool"
-			/>
-			<Tool
-				Name="VCCLCompilerTool"
-				AdditionalIncludeDirectories="../include,../../pjlib/include"
-				PreprocessorDefinitions="_CONSOLE;"
-				PrecompiledHeaderFile=""
-			/>
-			<Tool
-				Name="VCManagedResourceCompilerTool"
-			/>
-			<Tool
-				Name="VCResourceCompilerTool"
-			/>
-			<Tool
-				Name="VCPreLinkEventTool"
-			/>
-			<Tool
-				Name="VCLinkerTool"
-				AdditionalDependencies="netapi32.lib mswsock.lib ws2_32.lib odbc32.lib odbccp32.lib oleaut32.lib ole32.lib"
-				OutputFile="..\bin\pjlib-util-test-$(TargetCPU)-$(PlatformName)-vc$(VSVer)-$(ConfigurationName).exe"
-			/>
-			<Tool
-				Name="VCALinkTool"
-			/>
-			<Tool
-				Name="VCManifestTool"
-			/>
-			<Tool
-				Name="VCXDCMakeTool"
-			/>
-			<Tool
-				Name="VCBscMakeTool"
-			/>
-			<Tool
-				Name="VCFxCopTool"
-			/>
-			<Tool
-				Name="VCAppVerifierTool"
-			/>
-			<Tool
-				Name="VCWebDeploymentTool"
-			/>
-			<Tool
-				Name="VCPostBuildEventTool"
-			/>
-		</Configuration>
-		<Configuration
-			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"
+			Name="Debug-Static|Windows Mobile 6 Standard SDK (ARMV4I)"
+			ConfigurationType="1"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-common-defaults.vsprops"
 			ATLMinimizesCRunTimeLibraryUsage="false"
 			CharacterSet="1"
@@ -1497,5 +1152,5 @@
 				Name="VCLinkerTool"
 				AdditionalDependencies="ws2.lib"
-				OutputFile="..\bin\pjlib-util-test-$(TargetCPU)-wm2003ppc-vc$(VSVer)-$(ConfigurationName).exe"
+				OutputFile="..\bin\pjlib-util-test-$(TargetCPU)-wm6std-vc$(VSVer)-$(ConfigurationName).exe"
 			/>
 			<Tool
@@ -1524,7 +1179,7 @@
 		</Configuration>
 		<Configuration
-			Name="Release-Static|Smartphone 2003 (ARMV4)"
-			ConfigurationType="1"
-			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-release-defaults.vsprops"
+			Name="Debug-Static|Windows Mobile 6 Professional SDK (ARMV4I)"
+			ConfigurationType="1"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-common-defaults.vsprops"
 			ATLMinimizesCRunTimeLibraryUsage="false"
 			CharacterSet="1"
@@ -1563,5 +1218,5 @@
 				Name="VCLinkerTool"
 				AdditionalDependencies="ws2.lib"
-				OutputFile="..\bin\pjlib-util-test-$(TargetCPU)-wm2003sp-vc$(VSVer)-$(ConfigurationName).exe"
+				OutputFile="..\bin\pjlib-util-test-$(TargetCPU)-wm6pro-vc$(VSVer)-$(ConfigurationName).exe"
 			/>
 			<Tool
@@ -1590,7 +1245,7 @@
 		</Configuration>
 		<Configuration
-			Name="Release-Static|x64"
-			ConfigurationType="1"
-			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-win64-release-defaults.vsprops"
+			Name="Release-Dynamic|Win32"
+			ConfigurationType="1"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-win32-release-defaults.vsprops"
 			UseOfMFC="0"
 			ATLMinimizesCRunTimeLibraryUsage="false"
@@ -1611,5 +1266,4 @@
 			<Tool
 				Name="VCMIDLTool"
-				TargetEnvironment="3"
 			/>
 			<Tool
@@ -1632,5 +1286,4 @@
 				AdditionalDependencies="netapi32.lib mswsock.lib ws2_32.lib odbc32.lib odbccp32.lib oleaut32.lib ole32.lib"
 				OutputFile="..\bin\pjlib-util-test-$(TargetCPU)-$(PlatformName)-vc$(VSVer)-$(ConfigurationName).exe"
-				TargetMachine="17"
 			/>
 			<Tool
@@ -1660,9 +1313,9 @@
 		</Configuration>
 		<Configuration
-			Name="Release|Windows Mobile 6 Standard SDK (ARMV4I)"
-			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-release-defaults.vsprops"
+			Name="Release-Dynamic|Pocket PC 2003 (ARMV4)"
+			ConfigurationType="1"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-release-defaults.vsprops"
 			ATLMinimizesCRunTimeLibraryUsage="false"
 			CharacterSet="1"
-			ConfigurationType="1"
 			>
 			<Tool
@@ -1699,5 +1352,5 @@
 				Name="VCLinkerTool"
 				AdditionalDependencies="ws2.lib"
-				OutputFile="..\bin\pjlib-util-test-$(TargetCPU)-wm6std-vc$(VSVer)-$(ConfigurationName).exe"
+				OutputFile="..\bin\pjlib-util-test-$(TargetCPU)-wm2003ppc-vc$(VSVer)-$(ConfigurationName).exe"
 			/>
 			<Tool
@@ -1726,9 +1379,9 @@
 		</Configuration>
 		<Configuration
-			Name="Release|Windows Mobile 6 Professional SDK (ARMV4I)"
-			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-release-defaults.vsprops"
+			Name="Release-Dynamic|Smartphone 2003 (ARMV4)"
+			ConfigurationType="1"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-release-defaults.vsprops"
 			ATLMinimizesCRunTimeLibraryUsage="false"
 			CharacterSet="1"
-			ConfigurationType="1"
 			>
 			<Tool
@@ -1765,5 +1418,5 @@
 				Name="VCLinkerTool"
 				AdditionalDependencies="ws2.lib"
-				OutputFile="..\bin\pjlib-util-test-$(TargetCPU)-wm6pro-vc$(VSVer)-$(ConfigurationName).exe"
+				OutputFile="..\bin\pjlib-util-test-$(TargetCPU)-wm2003sp-vc$(VSVer)-$(ConfigurationName).exe"
 			/>
 			<Tool
@@ -1792,9 +1445,79 @@
 		</Configuration>
 		<Configuration
-			Name="Debug|Windows Mobile 6 Standard SDK (ARMV4I)"
-			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-common-defaults.vsprops"
+			Name="Release-Dynamic|x64"
+			ConfigurationType="1"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-win64-release-defaults.vsprops"
+			UseOfMFC="0"
+			ATLMinimizesCRunTimeLibraryUsage="false"
+			CharacterSet="2"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+				TargetEnvironment="3"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				AdditionalIncludeDirectories="../include,../../pjlib/include"
+				PreprocessorDefinitions="_CONSOLE;"
+				PrecompiledHeaderFile=""
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				AdditionalDependencies="netapi32.lib mswsock.lib ws2_32.lib odbc32.lib odbccp32.lib oleaut32.lib ole32.lib"
+				OutputFile="..\bin\pjlib-util-test-$(TargetCPU)-$(PlatformName)-vc$(VSVer)-$(ConfigurationName).exe"
+				TargetMachine="17"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCWebDeploymentTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release-Dynamic|Windows Mobile 6 Standard SDK (ARMV4I)"
+			ConfigurationType="1"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-release-defaults.vsprops"
 			ATLMinimizesCRunTimeLibraryUsage="false"
 			CharacterSet="1"
-			ConfigurationType="1"
 			>
 			<Tool
@@ -1858,9 +1581,9 @@
 		</Configuration>
 		<Configuration
-			Name="Debug|Windows Mobile 6 Professional SDK (ARMV4I)"
-			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-common-defaults.vsprops"
+			Name="Release-Dynamic|Windows Mobile 6 Professional SDK (ARMV4I)"
+			ConfigurationType="1"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-release-defaults.vsprops"
 			ATLMinimizesCRunTimeLibraryUsage="false"
 			CharacterSet="1"
-			ConfigurationType="1"
 			>
 			<Tool
@@ -1924,9 +1647,77 @@
 		</Configuration>
 		<Configuration
-			Name="Debug-Static|Windows Mobile 6 Standard SDK (ARMV4I)"
-			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-common-defaults.vsprops"
+			Name="Debug-Dynamic|Win32"
+			ConfigurationType="1"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-win32-common-defaults.vsprops"
+			UseOfMFC="0"
+			ATLMinimizesCRunTimeLibraryUsage="false"
+			CharacterSet="2"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				AdditionalIncludeDirectories="../include,../../pjlib/include"
+				PreprocessorDefinitions="_CONSOLE;"
+				PrecompiledHeaderFile=""
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				AdditionalDependencies="netapi32.lib mswsock.lib ws2_32.lib odbc32.lib odbccp32.lib oleaut32.lib ole32.lib"
+				OutputFile="..\bin\pjlib-util-test-$(TargetCPU)-$(PlatformName)-vc$(VSVer)-$(ConfigurationName).exe"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCWebDeploymentTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Debug-Dynamic|Pocket PC 2003 (ARMV4)"
+			ConfigurationType="1"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-common-defaults.vsprops"
 			ATLMinimizesCRunTimeLibraryUsage="false"
 			CharacterSet="1"
-			ConfigurationType="1"
 			>
 			<Tool
@@ -1963,5 +1754,5 @@
 				Name="VCLinkerTool"
 				AdditionalDependencies="ws2.lib"
-				OutputFile="..\bin\pjlib-util-test-$(TargetCPU)-wm6std-vc$(VSVer)-$(ConfigurationName).exe"
+				OutputFile="..\bin\pjlib-util-test-$(TargetCPU)-wm2003ppc-vc$(VSVer)-$(ConfigurationName).exe"
 			/>
 			<Tool
@@ -1990,9 +1781,9 @@
 		</Configuration>
 		<Configuration
-			Name="Debug-Static|Windows Mobile 6 Professional SDK (ARMV4I)"
-			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-common-defaults.vsprops"
+			Name="Debug-Dynamic|Smartphone 2003 (ARMV4)"
+			ConfigurationType="1"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-common-defaults.vsprops"
 			ATLMinimizesCRunTimeLibraryUsage="false"
 			CharacterSet="1"
-			ConfigurationType="1"
 			>
 			<Tool
@@ -2029,5 +1820,5 @@
 				Name="VCLinkerTool"
 				AdditionalDependencies="ws2.lib"
-				OutputFile="..\bin\pjlib-util-test-$(TargetCPU)-wm6pro-vc$(VSVer)-$(ConfigurationName).exe"
+				OutputFile="..\bin\pjlib-util-test-$(TargetCPU)-wm2003sp-vc$(VSVer)-$(ConfigurationName).exe"
 			/>
 			<Tool
@@ -2056,9 +1847,80 @@
 		</Configuration>
 		<Configuration
-			Name="Release-Dynamic|Windows Mobile 6 Standard SDK (ARMV4I)"
-			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-release-defaults.vsprops"
+			Name="Debug-Dynamic|x64"
+			ConfigurationType="1"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-win64-common-defaults.vsprops"
+			UseOfMFC="0"
+			ATLMinimizesCRunTimeLibraryUsage="false"
+			CharacterSet="2"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+				TargetEnvironment="3"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				AdditionalIncludeDirectories="../include,../../pjlib/include"
+				PreprocessorDefinitions="_CONSOLE;"
+				PrecompiledHeaderFile=""
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				AdditionalDependencies="netapi32.lib mswsock.lib ws2_32.lib odbc32.lib odbccp32.lib oleaut32.lib ole32.lib"
+				OutputFile="..\bin\pjlib-util-test-$(TargetCPU)-$(PlatformName)-vc$(VSVer)-$(ConfigurationName).exe"
+				TargetMachine="17"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCWebDeploymentTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Debug-Dynamic|Windows Mobile 6 Standard SDK (ARMV4I)"
+			ConfigurationType="1"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-common-defaults.vsprops"
 			ATLMinimizesCRunTimeLibraryUsage="false"
 			CharacterSet="1"
-			ConfigurationType="1"
 			>
 			<Tool
@@ -2122,9 +1984,9 @@
 		</Configuration>
 		<Configuration
-			Name="Release-Dynamic|Windows Mobile 6 Professional SDK (ARMV4I)"
-			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-release-defaults.vsprops"
+			Name="Debug-Dynamic|Windows Mobile 6 Professional SDK (ARMV4I)"
+			ConfigurationType="1"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-common-defaults.vsprops"
 			ATLMinimizesCRunTimeLibraryUsage="false"
 			CharacterSet="1"
-			ConfigurationType="1"
 			>
 			<Tool
@@ -2188,9 +2050,77 @@
 		</Configuration>
 		<Configuration
-			Name="Debug-Dynamic|Windows Mobile 6 Standard SDK (ARMV4I)"
-			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-common-defaults.vsprops"
+			Name="Release-Static|Win32"
+			ConfigurationType="1"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-win32-release-defaults.vsprops"
+			UseOfMFC="0"
+			ATLMinimizesCRunTimeLibraryUsage="false"
+			CharacterSet="2"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				AdditionalIncludeDirectories="../include,../../pjlib/include"
+				PreprocessorDefinitions="_CONSOLE;"
+				PrecompiledHeaderFile=""
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				AdditionalDependencies="netapi32.lib mswsock.lib ws2_32.lib odbc32.lib odbccp32.lib oleaut32.lib ole32.lib"
+				OutputFile="..\bin\pjlib-util-test-$(TargetCPU)-$(PlatformName)-vc$(VSVer)-$(ConfigurationName).exe"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCWebDeploymentTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			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"
 			ATLMinimizesCRunTimeLibraryUsage="false"
 			CharacterSet="1"
-			ConfigurationType="1"
 			>
 			<Tool
@@ -2227,5 +2157,5 @@
 				Name="VCLinkerTool"
 				AdditionalDependencies="ws2.lib"
-				OutputFile="..\bin\pjlib-util-test-$(TargetCPU)-wm6std-vc$(VSVer)-$(ConfigurationName).exe"
+				OutputFile="..\bin\pjlib-util-test-$(TargetCPU)-wm2003ppc-vc$(VSVer)-$(ConfigurationName).exe"
 			/>
 			<Tool
@@ -2254,9 +2184,9 @@
 		</Configuration>
 		<Configuration
-			Name="Debug-Dynamic|Windows Mobile 6 Professional SDK (ARMV4I)"
-			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-common-defaults.vsprops"
+			Name="Release-Static|Smartphone 2003 (ARMV4)"
+			ConfigurationType="1"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-release-defaults.vsprops"
 			ATLMinimizesCRunTimeLibraryUsage="false"
 			CharacterSet="1"
-			ConfigurationType="1"
 			>
 			<Tool
@@ -2293,5 +2223,5 @@
 				Name="VCLinkerTool"
 				AdditionalDependencies="ws2.lib"
-				OutputFile="..\bin\pjlib-util-test-$(TargetCPU)-wm6pro-vc$(VSVer)-$(ConfigurationName).exe"
+				OutputFile="..\bin\pjlib-util-test-$(TargetCPU)-wm2003sp-vc$(VSVer)-$(ConfigurationName).exe"
 			/>
 			<Tool
@@ -2320,9 +2250,79 @@
 		</Configuration>
 		<Configuration
+			Name="Release-Static|x64"
+			ConfigurationType="1"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-win64-release-defaults.vsprops"
+			UseOfMFC="0"
+			ATLMinimizesCRunTimeLibraryUsage="false"
+			CharacterSet="2"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+				TargetEnvironment="3"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				AdditionalIncludeDirectories="../include,../../pjlib/include"
+				PreprocessorDefinitions="_CONSOLE;"
+				PrecompiledHeaderFile=""
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				AdditionalDependencies="netapi32.lib mswsock.lib ws2_32.lib odbc32.lib odbccp32.lib oleaut32.lib ole32.lib"
+				OutputFile="..\bin\pjlib-util-test-$(TargetCPU)-$(PlatformName)-vc$(VSVer)-$(ConfigurationName).exe"
+				TargetMachine="17"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCWebDeploymentTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
 			Name="Release-Static|Windows Mobile 6 Standard SDK (ARMV4I)"
+			ConfigurationType="1"
 			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-release-defaults.vsprops"
 			ATLMinimizesCRunTimeLibraryUsage="false"
 			CharacterSet="1"
-			ConfigurationType="1"
 			>
 			<Tool
@@ -2387,8 +2387,8 @@
 		<Configuration
 			Name="Release-Static|Windows Mobile 6 Professional SDK (ARMV4I)"
+			ConfigurationType="1"
 			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-release-defaults.vsprops"
 			ATLMinimizesCRunTimeLibraryUsage="false"
 			CharacterSet="1"
-			ConfigurationType="1"
 			>
 			<Tool
@@ -3260,4 +3260,8 @@
 			</File>
 			<File
+				RelativePath="..\src\pjlib-util-test\json_test.c"
+				>
+			</File>
+			<File
 				RelativePath="..\src\pjlib-util-test\main.c"
 				>
@@ -3297,4 +3301,20 @@
 				</FileConfiguration>
 				<FileConfiguration
+					Name="Release|Windows Mobile 6 Standard SDK (ARMV4I)"
+					ExcludedFromBuild="true"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Release|Windows Mobile 6 Professional SDK (ARMV4I)"
+					ExcludedFromBuild="true"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+					/>
+				</FileConfiguration>
+				<FileConfiguration
 					Name="Debug|Win32"
 					>
@@ -3331,4 +3351,20 @@
 				</FileConfiguration>
 				<FileConfiguration
+					Name="Debug|Windows Mobile 6 Standard SDK (ARMV4I)"
+					ExcludedFromBuild="true"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Debug|Windows Mobile 6 Professional SDK (ARMV4I)"
+					ExcludedFromBuild="true"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+					/>
+				</FileConfiguration>
+				<FileConfiguration
 					Name="Debug-Static|Win32"
 					>
@@ -3365,4 +3401,20 @@
 				</FileConfiguration>
 				<FileConfiguration
+					Name="Debug-Static|Windows Mobile 6 Standard SDK (ARMV4I)"
+					ExcludedFromBuild="true"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Debug-Static|Windows Mobile 6 Professional SDK (ARMV4I)"
+					ExcludedFromBuild="true"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+					/>
+				</FileConfiguration>
+				<FileConfiguration
 					Name="Release-Dynamic|Win32"
 					>
@@ -3399,4 +3451,20 @@
 				</FileConfiguration>
 				<FileConfiguration
+					Name="Release-Dynamic|Windows Mobile 6 Standard SDK (ARMV4I)"
+					ExcludedFromBuild="true"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Release-Dynamic|Windows Mobile 6 Professional SDK (ARMV4I)"
+					ExcludedFromBuild="true"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+					/>
+				</FileConfiguration>
+				<FileConfiguration
 					Name="Debug-Dynamic|Win32"
 					>
@@ -3433,4 +3501,20 @@
 				</FileConfiguration>
 				<FileConfiguration
+					Name="Debug-Dynamic|Windows Mobile 6 Standard SDK (ARMV4I)"
+					ExcludedFromBuild="true"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Debug-Dynamic|Windows Mobile 6 Professional SDK (ARMV4I)"
+					ExcludedFromBuild="true"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+					/>
+				</FileConfiguration>
+				<FileConfiguration
 					Name="Release-Static|Win32"
 					>
@@ -3464,84 +3548,4 @@
 						AdditionalIncludeDirectories=""
 						PreprocessorDefinitions=""
-					/>
-				</FileConfiguration>
-				<FileConfiguration
-					Name="Release|Windows Mobile 6 Standard SDK (ARMV4I)"
-					ExcludedFromBuild="true"
-					>
-					<Tool
-						Name="VCCLCompilerTool"
-					/>
-				</FileConfiguration>
-				<FileConfiguration
-					Name="Release|Windows Mobile 6 Professional SDK (ARMV4I)"
-					ExcludedFromBuild="true"
-					>
-					<Tool
-						Name="VCCLCompilerTool"
-					/>
-				</FileConfiguration>
-				<FileConfiguration
-					Name="Debug|Windows Mobile 6 Standard SDK (ARMV4I)"
-					ExcludedFromBuild="true"
-					>
-					<Tool
-						Name="VCCLCompilerTool"
-					/>
-				</FileConfiguration>
-				<FileConfiguration
-					Name="Debug|Windows Mobile 6 Professional SDK (ARMV4I)"
-					ExcludedFromBuild="true"
-					>
-					<Tool
-						Name="VCCLCompilerTool"
-					/>
-				</FileConfiguration>
-				<FileConfiguration
-					Name="Debug-Static|Windows Mobile 6 Standard SDK (ARMV4I)"
-					ExcludedFromBuild="true"
-					>
-					<Tool
-						Name="VCCLCompilerTool"
-					/>
-				</FileConfiguration>
-				<FileConfiguration
-					Name="Debug-Static|Windows Mobile 6 Professional SDK (ARMV4I)"
-					ExcludedFromBuild="true"
-					>
-					<Tool
-						Name="VCCLCompilerTool"
-					/>
-				</FileConfiguration>
-				<FileConfiguration
-					Name="Release-Dynamic|Windows Mobile 6 Standard SDK (ARMV4I)"
-					ExcludedFromBuild="true"
-					>
-					<Tool
-						Name="VCCLCompilerTool"
-					/>
-				</FileConfiguration>
-				<FileConfiguration
-					Name="Release-Dynamic|Windows Mobile 6 Professional SDK (ARMV4I)"
-					ExcludedFromBuild="true"
-					>
-					<Tool
-						Name="VCCLCompilerTool"
-					/>
-				</FileConfiguration>
-				<FileConfiguration
-					Name="Debug-Dynamic|Windows Mobile 6 Standard SDK (ARMV4I)"
-					ExcludedFromBuild="true"
-					>
-					<Tool
-						Name="VCCLCompilerTool"
-					/>
-				</FileConfiguration>
-				<FileConfiguration
-					Name="Debug-Dynamic|Windows Mobile 6 Professional SDK (ARMV4I)"
-					ExcludedFromBuild="true"
-					>
-					<Tool
-						Name="VCCLCompilerTool"
 					/>
 				</FileConfiguration>
Index: /pjproject/trunk/pjlib-util/include/pjlib-util.h
===================================================================
--- /pjproject/trunk/pjlib-util/include/pjlib-util.h	(revision 4703)
+++ /pjproject/trunk/pjlib-util/include/pjlib-util.h	(revision 4704)
@@ -56,4 +56,7 @@
 #include <pjlib-util/xml.h>
 
+/* JSON */
+#include <pjlib-util/json.h>
+
 /* Old STUN */
 #include <pjlib-util/stun_simple.h>
Index: /pjproject/trunk/pjlib-util/include/pjlib-util/errno.h
===================================================================
--- /pjproject/trunk/pjlib-util/include/pjlib-util/errno.h	(revision 4703)
+++ /pjproject/trunk/pjlib-util/include/pjlib-util/errno.h	(revision 4704)
@@ -118,4 +118,13 @@
 
 
+/************************************************************
+ * JSON ERROR
+ ***********************************************************/
+/**
+ * @hideinitializer
+ * General invalid JSON message.
+ */
+#define PJLIB_UTIL_EINJSON	    (PJLIB_UTIL_ERRNO_START+30)	/* 320030 */
+
 
 /************************************************************
Index: /pjproject/trunk/pjlib-util/include/pjlib-util/json.h
===================================================================
--- /pjproject/trunk/pjlib-util/include/pjlib-util/json.h	(revision 4704)
+++ /pjproject/trunk/pjlib-util/include/pjlib-util/json.h	(revision 4704)
@@ -0,0 +1,228 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2013 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 __PJLIB_UTIL_JSON_H__
+#define __PJLIB_UTIL_JSON_H__
+
+
+/**
+ * @file json.h
+ * @brief PJLIB JSON Implementation
+ */
+
+#include <pj/types.h>
+#include <pj/list.h>
+#include <pj/pool.h>
+
+PJ_BEGIN_DECL
+
+/**
+ * @defgroup PJ_JSON JSON Writer and Loader
+ * @ingroup PJ_FILE_FMT
+ * @{
+ * This API implements JSON file format according to RFC 4627. It can be used
+ * to parse, write, and manipulate JSON documents.
+ */
+
+/**
+ * Type of JSON value.
+ */
+typedef enum pj_json_val_type
+{
+    PJ_JSON_VAL_NULL,		/**< Null value (null)			*/
+    PJ_JSON_VAL_BOOL,		/**< Boolean value (true, false)	*/
+    PJ_JSON_VAL_NUMBER,		/**< Numeric (float or fixed point)	*/
+    PJ_JSON_VAL_STRING,		/**< Literal string value.		*/
+    PJ_JSON_VAL_ARRAY,		/**< Array				*/
+    PJ_JSON_VAL_OBJ		/**< Object.				*/
+} pj_json_val_type;
+
+/* Forward declaration for JSON element */
+typedef struct pj_json_elem pj_json_elem;
+
+/**
+ * JSON list to store child elements.
+ */
+typedef struct pj_json_list
+{
+    PJ_DECL_LIST_MEMBER(pj_json_elem);
+} pj_json_list;
+
+/**
+ * This represents JSON element. A JSON element is basically a name/value
+ * pair, where the name is a string and the value can be one of null, boolean
+ * (true and false constants), number, string, array (containing zero or more
+ * elements), or object. An object can be viewed as C struct, that is a
+ * compound element containing other elements, each having name/value pair.
+ */
+struct pj_json_elem
+{
+    PJ_DECL_LIST_MEMBER(pj_json_elem);
+    pj_str_t		name;		/**< ELement name.		*/
+    pj_json_val_type	type;		/**< Element type.		*/
+    union
+    {
+	pj_bool_t	is_true;	/**< Boolean value.		*/
+	float		num;		/**< Number value.		*/
+	pj_str_t	str;		/**< String value.		*/
+	pj_json_list	children;	/**< Object and array children	*/
+    } value;				/**< Element value.		*/
+};
+
+/**
+ * Structure to be specified to pj_json_parse() to be filled with additional
+ * info when parsing failed.
+ */
+typedef struct pj_json_err_info
+{
+    unsigned	line;		/**< Line location of the error		*/
+    unsigned	col;		/**< Column location of the error	*/
+    int		err_char;	/**< The offending character.		*/
+} pj_json_err_info;
+
+/**
+ * Type of function callback to write JSON document in pj_json_writef().
+ *
+ * @param s		The string to be written to the document.
+ * @param size		The length of the string
+ * @param user_data	User data that was specified to pj_json_writef()
+ *
+ * @return		If the callback returns non-PJ_SUCCESS, it will
+ * 			stop the pj_json_writef() function and this error
+ * 			will be returned to caller.
+ */
+typedef pj_status_t (*pj_json_writer)(const char *s,
+				      unsigned size,
+				      void *user_data);
+
+/**
+ * Initialize null element.
+ *
+ * @param el		The element.
+ * @param name		Name to be given to the element, or NULL.
+ */
+PJ_DECL(void) pj_json_elem_null(pj_json_elem *el, pj_str_t *name);
+
+/**
+ * Initialize boolean element with the specified value.
+ *
+ * @param el		The element.
+ * @param name		Name to be given to the element, or NULL.
+ * @param val		The value.
+ */
+PJ_DECL(void) pj_json_elem_bool(pj_json_elem *el, pj_str_t *name,
+                                pj_bool_t val);
+
+/**
+ * Initialize number element with the specified value.
+ *
+ * @param el		The element.
+ * @param name		Name to be given to the element, or NULL.
+ * @param val		The value.
+ */
+PJ_DECL(void) pj_json_elem_number(pj_json_elem *el, pj_str_t *name,
+                                  float val);
+
+/**
+ * Initialize string element with the specified value.
+ *
+ * @param el		The element.
+ * @param name		Name to be given to the element, or NULL.
+ * @param val		The value.
+ */
+PJ_DECL(void) pj_json_elem_string(pj_json_elem *el, pj_str_t *name,
+                                  pj_str_t *val);
+
+/**
+ * Initialize element as an empty array
+ *
+ * @param el		The element.
+ * @param name		Name to be given to the element, or NULL.
+ */
+PJ_DECL(void) pj_json_elem_array(pj_json_elem *el, pj_str_t *name);
+
+/**
+ * Initialize element as an empty object
+ *
+ * @param el		The element.
+ * @param name		Name to be given to the element, or NULL.
+ */
+PJ_DECL(void) pj_json_elem_obj(pj_json_elem *el, pj_str_t *name);
+
+/**
+ * Add an element to an object or array.
+ *
+ * @param el		The object or array element.
+ * @param child		Element to be added to the object or array.
+ */
+PJ_DECL(void) pj_json_elem_add(pj_json_elem *el, pj_json_elem *child);
+
+/**
+ * Parse a JSON document in the buffer. The buffer MUST be NULL terminated,
+ * or if not then it must have enough size to put the NULL character.
+ *
+ * @param pool		The pool to allocate memory for creating elements.
+ * @param buffer	String buffer containing JSON document.
+ * @param size		Size of the document.
+ * @param err_info	Optional structure to be filled with info when
+ * 			parsing failed.
+ *
+ * @return		The root element from the document.
+ */
+PJ_DECL(pj_json_elem*) pj_json_parse(pj_pool_t *pool,
+                                     char *buffer,
+                                     unsigned *size,
+                                     pj_json_err_info *err_info);
+
+/**
+ * Write the specified element to the string buffer.
+ *
+ * @param elem		The element to be written.
+ * @param buffer	Output buffer.
+ * @param size		On input, it must be set to the size of the buffer.
+ * 			Upon successful return, this will be set to
+ * 			the length of the written string.
+ *
+ * @return		PJ_SUCCESS on success or the appropriate error.
+ */
+PJ_DECL(pj_status_t)   pj_json_write(const pj_json_elem *elem,
+                                     char *buffer, unsigned *size);
+
+/**
+ * Incrementally write the element to arbitrary medium using the specified
+ * callback to write the document chunks.
+ *
+ * @param elem		The element to be written.
+ * @param writer	Callback function which will be called to write
+ * 			text chunks.
+ * @param user_data	Arbitrary user data which will be given back when
+ * 			calling the callback.
+ *
+ * @return		PJ_SUCCESS on success or the appropriate error.
+ */
+PJ_DECL(pj_status_t)   pj_json_writef(const pj_json_elem *elem,
+                                      pj_json_writer writer,
+                                      void *user_data);
+
+/**
+ * @}
+ */
+
+PJ_END_DECL
+
+#endif	/* __PJLIB_UTIL_JSON_H__ */
Index: /pjproject/trunk/pjlib-util/src/pjlib-util-test/json_test.c
===================================================================
--- /pjproject/trunk/pjlib-util/src/pjlib-util-test/json_test.c	(revision 4704)
+++ /pjproject/trunk/pjlib-util/src/pjlib-util-test/json_test.c	(revision 4704)
@@ -0,0 +1,106 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2013 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 "test.h"
+
+#define THIS_FILE	"json_test.c"
+
+#if INCLUDE_JSON_TEST
+
+#include <pjlib-util/json.h>
+#include <pj/log.h>
+#include <pj/string.h>
+
+static char json_doc1[] =
+"{\
+    \"Object\": {\
+       \"Integer\":  800,\
+       \"Negative\":  -12,\
+       \"Float\": -7.2,\
+       \"String\":  \"A\\tString with tab\",\
+       \"Object2\": {\
+           \"True\": true,\
+           \"False\": false,\
+           \"Null\": null\
+       },\
+       \"Array1\": [116, false, \"string\", {}],\
+       \"Array2\": [\
+	    {\
+        	   \"Float\": 123.,\
+	    },\
+	    {\
+		   \"Float\": 123.,\
+	    }\
+       ]\
+     },\
+   \"Integer\":  800,\
+   \"Array1\": [116, false, \"string\"]\
+}\
+";
+
+static int json_verify_1()
+{
+    pj_pool_t *pool;
+    pj_json_elem *elem;
+    char *out_buf;
+    unsigned size;
+    pj_json_err_info err;
+
+    pool = pj_pool_create(mem, "json", 1000, 1000, NULL);
+
+    size = strlen(json_doc1);
+    elem = pj_json_parse(pool, json_doc1, &size, &err);
+    if (!elem) {
+	PJ_LOG(1, (THIS_FILE, "  Error: json_verify_1() parse error"));
+	goto on_error;
+    }
+
+    size = strlen(json_doc1) * 2;
+    out_buf = pj_pool_alloc(pool, size);
+
+    if (pj_json_write(elem, out_buf, &size)) {
+	PJ_LOG(1, (THIS_FILE, "  Error: json_verify_1() write error"));
+	goto on_error;
+    }
+
+    PJ_LOG(3,(THIS_FILE, "Json document:\n%s", out_buf));
+    pj_pool_release(pool);
+    return 0;
+
+on_error:
+    pj_pool_release(pool);
+    return 10;
+}
+
+
+int json_test(void)
+{
+    int rc;
+
+    rc = json_verify_1();
+    if (rc)
+	return rc;
+
+    return 0;
+}
+
+
+
+#else
+int json_dummy;
+#endif
Index: /pjproject/trunk/pjlib-util/src/pjlib-util-test/test.c
===================================================================
--- /pjproject/trunk/pjlib-util/src/pjlib-util-test/test.c	(revision 4703)
+++ /pjproject/trunk/pjlib-util/src/pjlib-util-test/test.c	(revision 4704)
@@ -73,4 +73,8 @@
 #endif
 
+#if INCLUDE_JSON_TEST
+    DO_TEST(json_test());
+#endif
+
 #if INCLUDE_ENCRYPTION_TEST
     DO_TEST(encryption_test());
Index: /pjproject/trunk/pjlib-util/src/pjlib-util-test/test.h
===================================================================
--- /pjproject/trunk/pjlib-util/src/pjlib-util-test/test.h	(revision 4703)
+++ /pjproject/trunk/pjlib-util/src/pjlib-util-test/test.h	(revision 4704)
@@ -21,4 +21,5 @@
 
 #define INCLUDE_XML_TEST	    1
+#define INCLUDE_JSON_TEST	    1
 #define INCLUDE_ENCRYPTION_TEST	    1
 #define INCLUDE_STUN_TEST	    1
@@ -27,4 +28,5 @@
 
 extern int xml_test(void);
+extern int json_test(void);
 extern int encryption_test();
 extern int encryption_benchmark();
Index: /pjproject/trunk/pjlib-util/src/pjlib-util/errno.c
===================================================================
--- /pjproject/trunk/pjlib-util/src/pjlib-util/errno.c	(revision 4703)
+++ /pjproject/trunk/pjlib-util/src/pjlib-util/errno.c	(revision 4704)
@@ -51,4 +51,7 @@
     /* XML errors */
     PJ_BUILD_ERR( PJLIB_UTIL_EINXML,		"Invalid XML message" ),
+
+    /* JSON errors */
+    PJ_BUILD_ERR( PJLIB_UTIL_EINJSON,		"Invalid JSON document" ),
 
     /* DNS errors */
Index: /pjproject/trunk/pjlib-util/src/pjlib-util/json.c
===================================================================
--- /pjproject/trunk/pjlib-util/src/pjlib-util/json.c	(revision 4704)
+++ /pjproject/trunk/pjlib-util/src/pjlib-util/json.c	(revision 4704)
@@ -0,0 +1,621 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2013 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 <pjlib-util/json.h>
+#include <pjlib-util/errno.h>
+#include <pjlib-util/scanner.h>
+#include <pj/assert.h>
+#include <pj/ctype.h>
+#include <pj/except.h>
+#include <pj/string.h>
+
+#define EL_INIT(p_el, nm, typ)	do { \
+				    if (nm) { \
+					p_el->name = *nm; \
+				    } else { \
+					p_el->name.ptr = (char*)""; \
+					p_el->name.slen = 0; \
+				    } \
+				    p_el->type = typ; \
+				} while (0)
+
+struct write_state;
+struct parse_state;
+
+#define NO_NAME	1
+
+static pj_status_t elem_write(const pj_json_elem *elem,
+                              struct write_state *st,
+                              unsigned flags);
+static pj_json_elem* parse_elem_throw(struct parse_state *st,
+                                      pj_json_elem *elem);
+
+
+PJ_DEF(void) pj_json_elem_null(pj_json_elem *el, pj_str_t *name)
+{
+    EL_INIT(el, name, PJ_JSON_VAL_NULL);
+}
+
+PJ_DEF(void) pj_json_elem_bool(pj_json_elem *el, pj_str_t *name,
+                                pj_bool_t val)
+{
+    EL_INIT(el, name, PJ_JSON_VAL_BOOL);
+    el->value.is_true = val;
+}
+
+PJ_DEF(void) pj_json_elem_number(pj_json_elem *el, pj_str_t *name,
+                                  float val)
+{
+    EL_INIT(el, name, PJ_JSON_VAL_NUMBER);
+    el->value.num = val;
+}
+
+PJ_DEF(void) pj_json_elem_string( pj_json_elem *el, pj_str_t *name,
+                                  pj_str_t *value)
+{
+    EL_INIT(el, name, PJ_JSON_VAL_STRING);
+    el->value.str = *value;
+}
+
+PJ_DEF(void) pj_json_elem_array(pj_json_elem *el, pj_str_t *name)
+{
+    EL_INIT(el, name, PJ_JSON_VAL_ARRAY);
+    pj_list_init(&el->value.children);
+}
+
+PJ_DEF(void) pj_json_elem_obj(pj_json_elem *el, pj_str_t *name)
+{
+    EL_INIT(el, name, PJ_JSON_VAL_OBJ);
+    pj_list_init(&el->value.children);
+}
+
+PJ_DEF(void) pj_json_elem_add(pj_json_elem *el, pj_json_elem *child)
+{
+    pj_assert(el->type == PJ_JSON_VAL_OBJ || el->type == PJ_JSON_VAL_ARRAY);
+    pj_list_push_back(&el->value.children, child);
+}
+
+struct parse_state
+{
+    pj_pool_t		*pool;
+    pj_scanner		 scanner;
+    pj_json_err_info 	*err_info;
+    pj_cis_t		 float_spec;	/* numbers with dot! */
+};
+
+static pj_status_t parse_children(struct parse_state *st,
+                                  pj_json_elem *parent)
+{
+    char end_quote = (parent->type == PJ_JSON_VAL_ARRAY)? ']' : '}';
+
+    pj_scan_get_char(&st->scanner);
+
+    while (*st->scanner.curptr != end_quote) {
+	pj_json_elem *child;
+
+	while (*st->scanner.curptr == ',')
+	    pj_scan_get_char(&st->scanner);
+
+	if (*st->scanner.curptr == end_quote)
+	    break;
+
+	child = parse_elem_throw(st, NULL);
+	if (!child)
+	    return PJLIB_UTIL_EINJSON;
+
+	pj_json_elem_add(parent, child);
+    }
+
+    pj_scan_get_char(&st->scanner);
+    return PJ_SUCCESS;
+}
+
+/* Return 0 if success or the index of the invalid char in the string */
+static unsigned parse_quoted_string(struct parse_state *st,
+                                    pj_str_t *output)
+{
+    pj_str_t token;
+    char *op, *ip, *iend;
+
+    pj_scan_get_quote(&st->scanner, '"', '"', &token);
+
+    /* Remove the quote characters */
+    token.ptr++;
+    token.slen-=2;
+
+    if (pj_strchr(&token, '\\') == NULL) {
+	*output = token;
+	return 0;
+    }
+
+    output->ptr = op = pj_pool_alloc(st->pool, token.slen);
+
+    ip = token.ptr;
+    iend = token.ptr + token.slen;
+
+    while (ip != iend) {
+	if (*ip == '\\') {
+	    ++ip;
+	    if (ip==iend) {
+		goto on_error;
+	    }
+	    if (*ip == 'u') {
+		ip++;
+		if (iend - ip < 4) {
+		    ip = iend -1;
+		    goto on_error;
+		}
+		/* Only use the last two hext digits because we're on
+		 * ASCII */
+		*op++ = (char)(pj_hex_digit_to_val(ip[2]) * 16 +
+			       pj_hex_digit_to_val(ip[3]));
+		ip += 4;
+	    } else if (*ip=='"' || *ip=='\\' || *ip=='/') {
+		*op++ = *ip++;
+	    } else if (*ip=='b') {
+		*op++ = '\b';
+		ip++;
+	    } else if (*ip=='f') {
+		*op++ = '\f';
+		ip++;
+	    } else if (*ip=='n') {
+		*op++ = '\n';
+		ip++;
+	    } else if (*ip=='r') {
+		*op++ = '\r';
+		ip++;
+	    } else if (*ip=='t') {
+		*op++ = '\t';
+		ip++;
+	    } else {
+		goto on_error;
+	    }
+	} else {
+	    *op++ = *ip++;
+	}
+    }
+
+    output->slen = op - output->ptr;
+    return 0;
+
+on_error:
+    output->slen = op - output->ptr;
+    return ip - token.ptr;
+}
+
+static pj_json_elem* parse_elem_throw(struct parse_state *st,
+                                      pj_json_elem *elem)
+{
+    pj_str_t name = {NULL, 0}, value = {NULL, 0};
+    pj_str_t token;
+
+    if (!elem)
+	elem = pj_pool_alloc(st->pool, sizeof(*elem));
+
+    /* Parse name */
+    if (*st->scanner.curptr == '"') {
+	pj_scan_get_char(&st->scanner);
+	pj_scan_get_until_ch(&st->scanner, '"', &token);
+	pj_scan_get_char(&st->scanner);
+
+	if (*st->scanner.curptr == ':') {
+	    pj_scan_get_char(&st->scanner);
+	    name = token;
+	} else {
+	    value = token;
+	}
+    }
+
+    if (value.slen) {
+	/* Element with string value and no name */
+	pj_json_elem_string(elem, &name, &value);
+	return elem;
+    }
+
+    /* Parse value */
+    if (pj_cis_match(&st->float_spec, *st->scanner.curptr) ||
+	*st->scanner.curptr == '-')
+    {
+	float val;
+	pj_bool_t neg = PJ_FALSE;
+
+	if (*st->scanner.curptr == '-') {
+	    pj_scan_get_char(&st->scanner);
+	    neg = PJ_TRUE;
+	}
+
+	pj_scan_get(&st->scanner, &st->float_spec, &token);
+	val = pj_strtof(&token);
+	if (neg) val = -val;
+
+	pj_json_elem_number(elem, &name, val);
+
+    } else if (*st->scanner.curptr == '"') {
+	unsigned err;
+	char *start = st->scanner.curptr;
+
+	err = parse_quoted_string(st, &token);
+	if (err) {
+	    st->scanner.curptr = start + err;
+	    return NULL;
+	}
+
+	pj_json_elem_string(elem, &name, &token);
+
+    } else if (pj_isalpha(*st->scanner.curptr)) {
+
+	if (pj_scan_strcmp(&st->scanner, "false", 5)==0) {
+	    pj_json_elem_bool(elem, &name, PJ_FALSE);
+	    pj_scan_advance_n(&st->scanner, 5, PJ_TRUE);
+	} else if (pj_scan_strcmp(&st->scanner, "true", 4)==0) {
+	    pj_json_elem_bool(elem, &name, PJ_TRUE);
+	    pj_scan_advance_n(&st->scanner, 4, PJ_TRUE);
+	} else if (pj_scan_strcmp(&st->scanner, "null", 4)==0) {
+	    pj_json_elem_null(elem, &name);
+	    pj_scan_advance_n(&st->scanner, 4, PJ_TRUE);
+	} else {
+	    return NULL;
+	}
+
+    } else if (*st->scanner.curptr == '[') {
+	pj_json_elem_array(elem, &name);
+	if (parse_children(st, elem) != PJ_SUCCESS)
+	    return NULL;
+
+    } else if (*st->scanner.curptr == '{') {
+	pj_json_elem_obj(elem, &name);
+	if (parse_children(st, elem) != PJ_SUCCESS)
+	    return NULL;
+
+    } else {
+	return NULL;
+    }
+
+    return elem;
+}
+
+static void on_syntax_error(pj_scanner *scanner)
+{
+    PJ_UNUSED_ARG(scanner);
+    PJ_THROW(11);
+}
+
+PJ_DEF(pj_json_elem*) pj_json_parse(pj_pool_t *pool,
+                                    char *buffer,
+                                    unsigned *size,
+                                    pj_json_err_info *err_info)
+{
+    pj_cis_buf_t cis_buf;
+    struct parse_state st;
+    pj_json_elem *root;
+    PJ_USE_EXCEPTION;
+
+    PJ_ASSERT_RETURN(pool && buffer && size, NULL);
+
+    if (!*size)
+	return NULL;
+
+    pj_bzero(&st, sizeof(st));
+    st.pool = pool;
+    st.err_info = err_info;
+    pj_scan_init(&st.scanner, buffer, *size,
+                 PJ_SCAN_AUTOSKIP_WS | PJ_SCAN_AUTOSKIP_NEWLINE,
+                 &on_syntax_error);
+    pj_cis_buf_init(&cis_buf);
+    pj_cis_init(&cis_buf, &st.float_spec);
+    pj_cis_add_str(&st.float_spec, ".0123456789");
+
+    PJ_TRY {
+	root = parse_elem_throw(&st, NULL);
+    }
+    PJ_CATCH_ANY {
+	root = NULL;
+    }
+    PJ_END
+
+    if (!root && err_info) {
+	err_info->line = st.scanner.line;
+	err_info->col = pj_scan_get_col(&st.scanner) + 1;
+	err_info->err_char = *st.scanner.curptr;
+    }
+
+    *size = (buffer + *size) - st.scanner.curptr;
+
+    pj_scan_fini(&st.scanner);
+
+    return root;
+}
+
+struct buf_writer_data
+{
+    char	*pos;
+    unsigned	 size;
+};
+
+static pj_status_t buf_writer(const char *s,
+			      unsigned size,
+			      void *user_data)
+{
+    struct buf_writer_data *buf_data = (struct buf_writer_data*)user_data;
+    if (size+1 >= buf_data->size)
+	return PJ_ETOOBIG;
+
+    pj_memcpy(buf_data->pos, s, size);
+    buf_data->pos += size;
+    buf_data->size -= size;
+
+    return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pj_json_write(const pj_json_elem *elem,
+                                  char *buffer, unsigned *size)
+{
+    struct buf_writer_data buf_data;
+    pj_status_t status;
+
+    PJ_ASSERT_RETURN(elem && buffer && size, PJ_EINVAL);
+
+    buf_data.pos = buffer;
+    buf_data.size = *size;
+
+    status = pj_json_writef(elem, &buf_writer, &buf_data);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    *buf_data.pos = '\0';
+    *size = (unsigned)(buf_data.pos - buffer);
+    return PJ_SUCCESS;
+}
+
+#define MAX_INDENT 		100
+#ifndef PJ_JSON_NAME_MIN_LEN
+#  define PJ_JSON_NAME_MIN_LEN	20
+#endif
+#define ESC_BUF_LEN		64
+#ifndef PJ_JSON_INDENT_SIZE
+#  define PJ_JSON_INDENT_SIZE	3
+#endif
+
+struct write_state
+{
+    pj_json_writer	 writer;
+    void 		*user_data;
+    char		 indent_buf[MAX_INDENT];
+    int			 indent;
+    char		 space[PJ_JSON_NAME_MIN_LEN];
+};
+
+#define CHECK(expr) do { \
+			status=expr; if (status!=PJ_SUCCESS) return status; } \
+		    while (0)
+
+static pj_status_t write_string_escaped(const pj_str_t *value,
+                                        struct write_state *st)
+{
+    const char *ip = value->ptr;
+    const char *iend = value->ptr + value->slen;
+    char buf[ESC_BUF_LEN];
+    char *op = buf;
+    char *oend = buf + ESC_BUF_LEN;
+    pj_status_t status;
+
+    while (ip != iend) {
+	/* Write to buffer to speedup writing instead of calling
+	 * the callback one by one for each character.
+	 */
+	while (ip != iend && op != oend) {
+	    if (oend - op < 2)
+		break;
+
+	    if (*ip == '"') {
+		*op++ = '\\';
+		*op++ = '"';
+		ip++;
+	    } else if (*ip == '\\') {
+		*op++ = '\\';
+		*op++ = '\\';
+		ip++;
+	    } else if (*ip == '/') {
+		*op++ = '\\';
+		*op++ = '/';
+		ip++;
+	    } else if (*ip == '\b') {
+		*op++ = '\\';
+		*op++ = 'b';
+		ip++;
+	    } else if (*ip == '\f') {
+		*op++ = '\\';
+		*op++ = 'f';
+		ip++;
+	    } else if (*ip == '\n') {
+		*op++ = '\\';
+		*op++ = 'n';
+		ip++;
+	    } else if (*ip == '\r') {
+		*op++ = '\\';
+		*op++ = 'r';
+		ip++;
+	    } else if (*ip == '\t') {
+		*op++ = '\\';
+		*op++ = 't';
+		ip++;
+	    } else if ((*ip >= 32 && *ip < 127)) {
+		/* unescaped */
+		*op++ = *ip++;
+	    } else {
+		/* escaped */
+		if (oend - op < 6)
+		    break;
+		*op++ = '\\';
+		*op++ = 'u';
+		*op++ = '0';
+		*op++ = '0';
+		pj_val_to_hex_digit(*ip, op);
+		op+=2;
+		ip++;
+	    }
+	}
+
+	CHECK( st->writer( buf, op-buf, st->user_data) );
+	op = buf;
+    }
+
+    return PJ_SUCCESS;
+}
+
+static pj_status_t write_children(const pj_json_list *list,
+                                  const char quotes[2],
+                                  struct write_state *st)
+{
+    unsigned flags = (quotes[0]=='[') ? NO_NAME : 0;
+    pj_status_t status;
+
+    //CHECK( st->writer( st->indent_buf, st->indent, st->user_data) );
+    CHECK( st->writer( &quotes[0], 1, st->user_data) );
+    CHECK( st->writer( " ", 1, st->user_data) );
+
+    if (!pj_list_empty(list)) {
+	pj_bool_t indent_added = PJ_FALSE;
+	pj_json_elem *child = list->next;
+
+	if (child->name.slen == 0) {
+	    /* Simple list */
+	    while (child != (pj_json_elem*)list) {
+		status = elem_write(child, st, flags);
+		if (status != PJ_SUCCESS)
+		    return status;
+
+		if (child->next != (pj_json_elem*)list)
+		    CHECK( st->writer( ", ", 2, st->user_data) );
+		child = child->next;
+	    }
+	} else {
+	    if (st->indent < sizeof(st->indent_buf)) {
+		st->indent += PJ_JSON_INDENT_SIZE;
+		indent_added = PJ_TRUE;
+	    }
+	    CHECK( st->writer( "\n", 1, st->user_data) );
+	    while (child != (pj_json_elem*)list) {
+		status = elem_write(child, st, flags);
+		if (status != PJ_SUCCESS)
+		    return status;
+
+		if (child->next != (pj_json_elem*)list)
+		    CHECK( st->writer( ",\n", 2, st->user_data) );
+		else
+		    CHECK( st->writer( "\n", 1, st->user_data) );
+		child = child->next;
+	    }
+	    if (indent_added) {
+		st->indent -= PJ_JSON_INDENT_SIZE;
+	    }
+	    CHECK( st->writer( st->indent_buf, st->indent, st->user_data) );
+	}
+    }
+    CHECK( st->writer( &quotes[1], 1, st->user_data) );
+
+    return PJ_SUCCESS;
+}
+
+static pj_status_t elem_write(const pj_json_elem *elem,
+                              struct write_state *st,
+                              unsigned flags)
+{
+    pj_status_t status;
+
+    if (elem->name.slen) {
+	CHECK( st->writer( st->indent_buf, st->indent, st->user_data) );
+	if ((flags & NO_NAME)==0) {
+	    CHECK( st->writer( "\"", 1, st->user_data) );
+	    CHECK( write_string_escaped(&elem->name, st) );
+	    CHECK( st->writer( "\": ", 3, st->user_data) );
+	    if (elem->name.slen < PJ_JSON_NAME_MIN_LEN /*&&
+		elem->type != PJ_JSON_VAL_OBJ &&
+		elem->type != PJ_JSON_VAL_ARRAY*/)
+	    {
+		CHECK( st->writer( st->space,
+		                   PJ_JSON_NAME_MIN_LEN - elem->name.slen,
+				   st->user_data) );
+	    }
+	}
+    }
+
+    switch (elem->type) {
+    case PJ_JSON_VAL_NULL:
+	CHECK( st->writer( "null", 4, st->user_data) );
+	break;
+    case PJ_JSON_VAL_BOOL:
+	if (elem->value.is_true)
+	    CHECK( st->writer( "true", 4, st->user_data) );
+	else
+	    CHECK( st->writer( "false", 5, st->user_data) );
+	break;
+    case PJ_JSON_VAL_NUMBER:
+	{
+	    char num_buf[65];
+	    int len;
+
+	    if (elem->value.num == (int)elem->value.num)
+		len = pj_ansi_snprintf(num_buf, sizeof(num_buf), "%d",
+		                       (int)elem->value.num);
+	    else
+		len = pj_ansi_snprintf(num_buf, sizeof(num_buf), "%f",
+		                       elem->value.num);
+
+	    if (len < 0 || len >= sizeof(num_buf))
+		return PJ_ETOOBIG;
+	    CHECK( st->writer( num_buf, len, st->user_data) );
+	}
+	break;
+    case PJ_JSON_VAL_STRING:
+	CHECK( st->writer( "\"", 1, st->user_data) );
+	CHECK( write_string_escaped( &elem->value.str, st) );
+	CHECK( st->writer( "\"", 1, st->user_data) );
+	break;
+    case PJ_JSON_VAL_ARRAY:
+	CHECK( write_children(&elem->value.children, "[]", st) );
+	break;
+    case PJ_JSON_VAL_OBJ:
+	CHECK( write_children(&elem->value.children, "{}", st) );
+	break;
+    default:
+	pj_assert(!"Unhandled value type");
+    }
+
+    return PJ_SUCCESS;
+}
+
+#undef CHECK
+
+PJ_DEF(pj_status_t) pj_json_writef( const pj_json_elem *elem,
+                                    pj_json_writer writer,
+                                    void *user_data)
+{
+    struct write_state st;
+
+    PJ_ASSERT_RETURN(elem && writer, PJ_EINVAL);
+
+    st.writer 		= writer;
+    st.user_data	= user_data,
+    st.indent 		= 0;
+    pj_memset(st.indent_buf, ' ', MAX_INDENT);
+    pj_memset(st.space, ' ', PJ_JSON_NAME_MIN_LEN);
+
+    return elem_write(elem, &st, 0);
+}
+
Index: /pjproject/trunk/pjlib/build/os-auto.mak.in
===================================================================
--- /pjproject/trunk/pjlib/build/os-auto.mak.in	(revision 4703)
+++ /pjproject/trunk/pjlib/build/os-auto.mak.in	(revision 4704)
@@ -24,5 +24,6 @@
 # Additional LDFLAGS for pjlib-test
 #
-export TEST_LDFLAGS += @LDFLAGS@ @LIBS@
+# Disabled, as this causes duplicated LDFLAGS, which may raise linking errors
+#export TEST_LDFLAGS += @LDFLAGS@ @LIBS@
 
 #
Index: /pjproject/trunk/pjlib/include/pj/compat/cc_gcc.h
===================================================================
--- /pjproject/trunk/pjlib/include/pj/compat/cc_gcc.h	(revision 4703)
+++ /pjproject/trunk/pjlib/include/pj/compat/cc_gcc.h	(revision 4704)
@@ -64,5 +64,5 @@
 
 #define PJ_INT64(val)		val##LL
-#define PJ_UINT64(val)		val##LLU
+#define PJ_UINT64(val)		val##ULL
 #define PJ_INT64_FMT		"L"
 
Index: /pjproject/trunk/pjlib/include/pj/string.h
===================================================================
--- /pjproject/trunk/pjlib/include/pj/string.h	(revision 4703)
+++ /pjproject/trunk/pjlib/include/pj/string.h	(revision 4704)
@@ -576,4 +576,13 @@
 
 /**
+ * Convert string to float.
+ *
+ * @param str	the string.
+ *
+ * @return the value.
+ */
+PJ_DECL(float) pj_strtof(const pj_str_t *str);
+
+/**
  * Utility to convert unsigned integer to string. Note that the
  * string will be NULL terminated.
Index: /pjproject/trunk/pjlib/include/pj/types.h
===================================================================
--- /pjproject/trunk/pjlib/include/pj/types.h	(revision 4703)
+++ /pjproject/trunk/pjlib/include/pj/types.h	(revision 4704)
@@ -87,13 +87,16 @@
 #endif
 
-
-/** Status is OK. */
-#define PJ_SUCCESS  0
-
-/** True value. */
-#define PJ_TRUE	    1
-
-/** False value. */
-#define PJ_FALSE    0
+/** Some constants */
+enum pj_constants_
+{
+    /** Status is OK. */
+    PJ_SUCCESS=0,
+
+    /** True value. */
+    PJ_TRUE=1,
+
+    /** False value. */
+    PJ_FALSE=0
+};
 
 /**
Index: /pjproject/trunk/pjlib/src/pj/string.c
===================================================================
--- /pjproject/trunk/pjlib/src/pj/string.c	(revision 4703)
+++ /pjproject/trunk/pjlib/src/pj/string.c	(revision 4704)
@@ -176,4 +176,42 @@
 }
 
+PJ_DEF(float) pj_strtof(const pj_str_t *str)
+{
+    pj_str_t part;
+    char *pdot;
+    float val;
+
+    if (str->slen == 0)
+	return 0;
+
+    pdot = (char*)pj_memchr(str->ptr, '.', str->slen);
+    part.ptr = str->ptr;
+    part.slen = pdot ? pdot - str->ptr : str->slen;
+
+    if (part.slen)
+	val = (float)pj_strtol(&part);
+    else
+	val = 0;
+
+    if (pdot) {
+	part.ptr = pdot + 1;
+	part.slen = (str->ptr + str->slen - pdot - 1);
+	if (part.slen) {
+	    pj_str_t endptr;
+	    float fpart, fdiv;
+	    int i;
+	    fpart = (float)pj_strtoul2(&part, &endptr, 10);
+	    fdiv = 1.0;
+	    for (i=0; i<(part.slen - endptr.slen); ++i)
+		    fdiv = fdiv * 10;
+	    if (val >= 0)
+		val += (fpart / fdiv);
+	    else
+		val -= (fpart / fdiv);
+	}
+    }
+    return val;
+}
+
 PJ_DEF(int) pj_utoa(unsigned long val, char *buf)
 {
Index: /pjproject/trunk/pjmedia/include/pjmedia-videodev/videodev.h
===================================================================
--- /pjproject/trunk/pjmedia/include/pjmedia-videodev/videodev.h	(revision 4703)
+++ /pjproject/trunk/pjmedia/include/pjmedia-videodev/videodev.h	(revision 4704)
@@ -132,5 +132,5 @@
  * Device index constants.
  */
-enum
+enum pjmedia_vid_dev_std_index
 {
     /**
Index: /pjproject/trunk/pjproject-vs8.sln
===================================================================
--- /pjproject/trunk/pjproject-vs8.sln	(revision 4703)
+++ /pjproject/trunk/pjproject-vs8.sln	(revision 4704)
@@ -36,4 +36,5 @@
 	ProjectSection(ProjectDependencies) = postProject
 		{23D7679C-764C-4E02-8B29-BB882CEEEFE2} = {23D7679C-764C-4E02-8B29-BB882CEEEFE2}
+		{B82CDD25-6903-430E-BD38-D8129A2015C1} = {B82CDD25-6903-430E-BD38-D8129A2015C1}
 	EndProjectSection
 EndProject
@@ -101,4 +102,5 @@
 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libpjproject", "pjsip-apps\build\libpjproject.vcproj", "{23D7679C-764C-4E02-8B29-BB882CEEEFE2}"
 	ProjectSection(ProjectDependencies) = postProject
+		{B82CDD25-6903-430E-BD38-D8129A2015C1} = {B82CDD25-6903-430E-BD38-D8129A2015C1}
 		{2BB84911-C1B4-4747-B93D-36AA82CC5031} = {2BB84911-C1B4-4747-B93D-36AA82CC5031}
 		{E8A3F6FA-AE1C-4C8E-A0B6-9C8480324EAA} = {E8A3F6FA-AE1C-4C8E-A0B6-9C8480324EAA}
@@ -117,8 +119,8 @@
 		{DA0E03ED-53A7-4050-8A85-90541C5509F8} = {DA0E03ED-53A7-4050-8A85-90541C5509F8}
 		{B5FE16F8-3EDB-4110-BD80-B4238CC01E8D} = {B5FE16F8-3EDB-4110-BD80-B4238CC01E8D}
+		{9CA0FDFB-2172-41FC-B7F1-5CE915EDCB37} = {9CA0FDFB-2172-41FC-B7F1-5CE915EDCB37}
 		{E53AA5FF-B737-40AA-BD13-387EFA99023D} = {E53AA5FF-B737-40AA-BD13-387EFA99023D}
 		{A1989FF3-9894-40F4-B5A6-6EA364476E45} = {A1989FF3-9894-40F4-B5A6-6EA364476E45}
 		{F0DBAA03-1BA3-4E3B-A2CA-727E3D3AB858} = {F0DBAA03-1BA3-4E3B-A2CA-727E3D3AB858}
-		{9CA0FDFB-2172-41FC-B7F1-5CE915EDCB37} = {9CA0FDFB-2172-41FC-B7F1-5CE915EDCB37}
 	EndProjectSection
 EndProject
@@ -136,4 +138,6 @@
 EndProject
 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libbaseclasses", "third_party\build\baseclasses\libbaseclasses.vcproj", "{E8A3F6FA-AE1C-4C8E-A0B6-9C8480324EAA}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pjsua2_lib", "pjsip\build\pjsua2_lib.vcproj", "{B82CDD25-6903-430E-BD38-D8129A2015C1}"
 EndProject
 Global
@@ -3173,4 +3177,112 @@
 		{E8A3F6FA-AE1C-4C8E-A0B6-9C8480324EAA}.Release-Static|x64.ActiveCfg = Release-Static|x64
 		{E8A3F6FA-AE1C-4C8E-A0B6-9C8480324EAA}.Release-Static|x64.Build.0 = Release-Static|x64
+		{B82CDD25-6903-430E-BD38-D8129A2015C1}.Debug|Pocket PC 2003 (ARMV4).ActiveCfg = Debug|Pocket PC 2003 (ARMV4)
+		{B82CDD25-6903-430E-BD38-D8129A2015C1}.Debug|Pocket PC 2003 (ARMV4).Build.0 = Debug|Pocket PC 2003 (ARMV4)
+		{B82CDD25-6903-430E-BD38-D8129A2015C1}.Debug|Pocket PC 2003 (ARMV4).Deploy.0 = Debug|Pocket PC 2003 (ARMV4)
+		{B82CDD25-6903-430E-BD38-D8129A2015C1}.Debug|Smartphone 2003 (ARMV4).ActiveCfg = Debug|Smartphone 2003 (ARMV4)
+		{B82CDD25-6903-430E-BD38-D8129A2015C1}.Debug|Smartphone 2003 (ARMV4).Build.0 = Debug|Smartphone 2003 (ARMV4)
+		{B82CDD25-6903-430E-BD38-D8129A2015C1}.Debug|Smartphone 2003 (ARMV4).Deploy.0 = Debug|Smartphone 2003 (ARMV4)
+		{B82CDD25-6903-430E-BD38-D8129A2015C1}.Debug|Win32.ActiveCfg = Debug|Win32
+		{B82CDD25-6903-430E-BD38-D8129A2015C1}.Debug|Win32.Build.0 = Debug|Win32
+		{B82CDD25-6903-430E-BD38-D8129A2015C1}.Debug|Windows Mobile 5.0 Pocket PC SDK (ARMV4I).ActiveCfg = Debug|Windows Mobile 6 Professional SDK (ARMV4I)
+		{B82CDD25-6903-430E-BD38-D8129A2015C1}.Debug|Windows Mobile 5.0 Smartphone SDK (ARMV4I).ActiveCfg = Debug|Windows Mobile 6 Professional SDK (ARMV4I)
+		{B82CDD25-6903-430E-BD38-D8129A2015C1}.Debug|Windows Mobile 6 Professional SDK (ARMV4I).ActiveCfg = Debug|Windows Mobile 6 Professional SDK (ARMV4I)
+		{B82CDD25-6903-430E-BD38-D8129A2015C1}.Debug|Windows Mobile 6 Professional SDK (ARMV4I).Build.0 = Debug|Windows Mobile 6 Professional SDK (ARMV4I)
+		{B82CDD25-6903-430E-BD38-D8129A2015C1}.Debug|Windows Mobile 6 Professional SDK (ARMV4I).Deploy.0 = Debug|Windows Mobile 6 Professional SDK (ARMV4I)
+		{B82CDD25-6903-430E-BD38-D8129A2015C1}.Debug|Windows Mobile 6 Standard SDK (ARMV4I).ActiveCfg = Debug|Windows Mobile 6 Standard SDK (ARMV4I)
+		{B82CDD25-6903-430E-BD38-D8129A2015C1}.Debug|Windows Mobile 6 Standard SDK (ARMV4I).Build.0 = Debug|Windows Mobile 6 Standard SDK (ARMV4I)
+		{B82CDD25-6903-430E-BD38-D8129A2015C1}.Debug|Windows Mobile 6 Standard SDK (ARMV4I).Deploy.0 = Debug|Windows Mobile 6 Standard SDK (ARMV4I)
+		{B82CDD25-6903-430E-BD38-D8129A2015C1}.Debug|x64.ActiveCfg = Debug|x64
+		{B82CDD25-6903-430E-BD38-D8129A2015C1}.Debug|x64.Build.0 = Debug|x64
+		{B82CDD25-6903-430E-BD38-D8129A2015C1}.Debug-Dynamic|Pocket PC 2003 (ARMV4).ActiveCfg = Debug-Dynamic|Pocket PC 2003 (ARMV4)
+		{B82CDD25-6903-430E-BD38-D8129A2015C1}.Debug-Dynamic|Pocket PC 2003 (ARMV4).Build.0 = Debug-Dynamic|Pocket PC 2003 (ARMV4)
+		{B82CDD25-6903-430E-BD38-D8129A2015C1}.Debug-Dynamic|Pocket PC 2003 (ARMV4).Deploy.0 = Debug-Dynamic|Pocket PC 2003 (ARMV4)
+		{B82CDD25-6903-430E-BD38-D8129A2015C1}.Debug-Dynamic|Smartphone 2003 (ARMV4).ActiveCfg = Debug-Dynamic|Smartphone 2003 (ARMV4)
+		{B82CDD25-6903-430E-BD38-D8129A2015C1}.Debug-Dynamic|Smartphone 2003 (ARMV4).Build.0 = Debug-Dynamic|Smartphone 2003 (ARMV4)
+		{B82CDD25-6903-430E-BD38-D8129A2015C1}.Debug-Dynamic|Smartphone 2003 (ARMV4).Deploy.0 = Debug-Dynamic|Smartphone 2003 (ARMV4)
+		{B82CDD25-6903-430E-BD38-D8129A2015C1}.Debug-Dynamic|Win32.ActiveCfg = Debug-Dynamic|Win32
+		{B82CDD25-6903-430E-BD38-D8129A2015C1}.Debug-Dynamic|Win32.Build.0 = Debug-Dynamic|Win32
+		{B82CDD25-6903-430E-BD38-D8129A2015C1}.Debug-Dynamic|Windows Mobile 5.0 Pocket PC SDK (ARMV4I).ActiveCfg = Debug-Dynamic|Windows Mobile 6 Professional SDK (ARMV4I)
+		{B82CDD25-6903-430E-BD38-D8129A2015C1}.Debug-Dynamic|Windows Mobile 5.0 Smartphone SDK (ARMV4I).ActiveCfg = Debug-Dynamic|Windows Mobile 6 Professional SDK (ARMV4I)
+		{B82CDD25-6903-430E-BD38-D8129A2015C1}.Debug-Dynamic|Windows Mobile 6 Professional SDK (ARMV4I).ActiveCfg = Debug-Dynamic|Windows Mobile 6 Professional SDK (ARMV4I)
+		{B82CDD25-6903-430E-BD38-D8129A2015C1}.Debug-Dynamic|Windows Mobile 6 Professional SDK (ARMV4I).Build.0 = Debug-Dynamic|Windows Mobile 6 Professional SDK (ARMV4I)
+		{B82CDD25-6903-430E-BD38-D8129A2015C1}.Debug-Dynamic|Windows Mobile 6 Professional SDK (ARMV4I).Deploy.0 = Debug-Dynamic|Windows Mobile 6 Professional SDK (ARMV4I)
+		{B82CDD25-6903-430E-BD38-D8129A2015C1}.Debug-Dynamic|Windows Mobile 6 Standard SDK (ARMV4I).ActiveCfg = Debug-Dynamic|Windows Mobile 6 Standard SDK (ARMV4I)
+		{B82CDD25-6903-430E-BD38-D8129A2015C1}.Debug-Dynamic|Windows Mobile 6 Standard SDK (ARMV4I).Build.0 = Debug-Dynamic|Windows Mobile 6 Standard SDK (ARMV4I)
+		{B82CDD25-6903-430E-BD38-D8129A2015C1}.Debug-Dynamic|Windows Mobile 6 Standard SDK (ARMV4I).Deploy.0 = Debug-Dynamic|Windows Mobile 6 Standard SDK (ARMV4I)
+		{B82CDD25-6903-430E-BD38-D8129A2015C1}.Debug-Dynamic|x64.ActiveCfg = Debug-Dynamic|x64
+		{B82CDD25-6903-430E-BD38-D8129A2015C1}.Debug-Dynamic|x64.Build.0 = Debug-Dynamic|x64
+		{B82CDD25-6903-430E-BD38-D8129A2015C1}.Debug-Static|Pocket PC 2003 (ARMV4).ActiveCfg = Debug-Static|Pocket PC 2003 (ARMV4)
+		{B82CDD25-6903-430E-BD38-D8129A2015C1}.Debug-Static|Pocket PC 2003 (ARMV4).Build.0 = Debug-Static|Pocket PC 2003 (ARMV4)
+		{B82CDD25-6903-430E-BD38-D8129A2015C1}.Debug-Static|Pocket PC 2003 (ARMV4).Deploy.0 = Debug-Static|Pocket PC 2003 (ARMV4)
+		{B82CDD25-6903-430E-BD38-D8129A2015C1}.Debug-Static|Smartphone 2003 (ARMV4).ActiveCfg = Debug-Static|Smartphone 2003 (ARMV4)
+		{B82CDD25-6903-430E-BD38-D8129A2015C1}.Debug-Static|Smartphone 2003 (ARMV4).Build.0 = Debug-Static|Smartphone 2003 (ARMV4)
+		{B82CDD25-6903-430E-BD38-D8129A2015C1}.Debug-Static|Smartphone 2003 (ARMV4).Deploy.0 = Debug-Static|Smartphone 2003 (ARMV4)
+		{B82CDD25-6903-430E-BD38-D8129A2015C1}.Debug-Static|Win32.ActiveCfg = Debug-Static|Win32
+		{B82CDD25-6903-430E-BD38-D8129A2015C1}.Debug-Static|Win32.Build.0 = Debug-Static|Win32
+		{B82CDD25-6903-430E-BD38-D8129A2015C1}.Debug-Static|Windows Mobile 5.0 Pocket PC SDK (ARMV4I).ActiveCfg = Debug-Static|Windows Mobile 6 Professional SDK (ARMV4I)
+		{B82CDD25-6903-430E-BD38-D8129A2015C1}.Debug-Static|Windows Mobile 5.0 Smartphone SDK (ARMV4I).ActiveCfg = Debug-Static|Windows Mobile 6 Professional SDK (ARMV4I)
+		{B82CDD25-6903-430E-BD38-D8129A2015C1}.Debug-Static|Windows Mobile 6 Professional SDK (ARMV4I).ActiveCfg = Debug-Static|Windows Mobile 6 Professional SDK (ARMV4I)
+		{B82CDD25-6903-430E-BD38-D8129A2015C1}.Debug-Static|Windows Mobile 6 Professional SDK (ARMV4I).Build.0 = Debug-Static|Windows Mobile 6 Professional SDK (ARMV4I)
+		{B82CDD25-6903-430E-BD38-D8129A2015C1}.Debug-Static|Windows Mobile 6 Professional SDK (ARMV4I).Deploy.0 = Debug-Static|Windows Mobile 6 Professional SDK (ARMV4I)
+		{B82CDD25-6903-430E-BD38-D8129A2015C1}.Debug-Static|Windows Mobile 6 Standard SDK (ARMV4I).ActiveCfg = Debug-Static|Windows Mobile 6 Standard SDK (ARMV4I)
+		{B82CDD25-6903-430E-BD38-D8129A2015C1}.Debug-Static|Windows Mobile 6 Standard SDK (ARMV4I).Build.0 = Debug-Static|Windows Mobile 6 Standard SDK (ARMV4I)
+		{B82CDD25-6903-430E-BD38-D8129A2015C1}.Debug-Static|Windows Mobile 6 Standard SDK (ARMV4I).Deploy.0 = Debug-Static|Windows Mobile 6 Standard SDK (ARMV4I)
+		{B82CDD25-6903-430E-BD38-D8129A2015C1}.Debug-Static|x64.ActiveCfg = Debug-Static|x64
+		{B82CDD25-6903-430E-BD38-D8129A2015C1}.Debug-Static|x64.Build.0 = Debug-Static|x64
+		{B82CDD25-6903-430E-BD38-D8129A2015C1}.Release|Pocket PC 2003 (ARMV4).ActiveCfg = Release|Pocket PC 2003 (ARMV4)
+		{B82CDD25-6903-430E-BD38-D8129A2015C1}.Release|Pocket PC 2003 (ARMV4).Build.0 = Release|Pocket PC 2003 (ARMV4)
+		{B82CDD25-6903-430E-BD38-D8129A2015C1}.Release|Pocket PC 2003 (ARMV4).Deploy.0 = Release|Pocket PC 2003 (ARMV4)
+		{B82CDD25-6903-430E-BD38-D8129A2015C1}.Release|Smartphone 2003 (ARMV4).ActiveCfg = Release|Smartphone 2003 (ARMV4)
+		{B82CDD25-6903-430E-BD38-D8129A2015C1}.Release|Smartphone 2003 (ARMV4).Build.0 = Release|Smartphone 2003 (ARMV4)
+		{B82CDD25-6903-430E-BD38-D8129A2015C1}.Release|Smartphone 2003 (ARMV4).Deploy.0 = Release|Smartphone 2003 (ARMV4)
+		{B82CDD25-6903-430E-BD38-D8129A2015C1}.Release|Win32.ActiveCfg = Release|Win32
+		{B82CDD25-6903-430E-BD38-D8129A2015C1}.Release|Win32.Build.0 = Release|Win32
+		{B82CDD25-6903-430E-BD38-D8129A2015C1}.Release|Windows Mobile 5.0 Pocket PC SDK (ARMV4I).ActiveCfg = Release|Windows Mobile 6 Professional SDK (ARMV4I)
+		{B82CDD25-6903-430E-BD38-D8129A2015C1}.Release|Windows Mobile 5.0 Smartphone SDK (ARMV4I).ActiveCfg = Release|Windows Mobile 6 Professional SDK (ARMV4I)
+		{B82CDD25-6903-430E-BD38-D8129A2015C1}.Release|Windows Mobile 6 Professional SDK (ARMV4I).ActiveCfg = Release|Windows Mobile 6 Professional SDK (ARMV4I)
+		{B82CDD25-6903-430E-BD38-D8129A2015C1}.Release|Windows Mobile 6 Professional SDK (ARMV4I).Build.0 = Release|Windows Mobile 6 Professional SDK (ARMV4I)
+		{B82CDD25-6903-430E-BD38-D8129A2015C1}.Release|Windows Mobile 6 Professional SDK (ARMV4I).Deploy.0 = Release|Windows Mobile 6 Professional SDK (ARMV4I)
+		{B82CDD25-6903-430E-BD38-D8129A2015C1}.Release|Windows Mobile 6 Standard SDK (ARMV4I).ActiveCfg = Release|Windows Mobile 6 Standard SDK (ARMV4I)
+		{B82CDD25-6903-430E-BD38-D8129A2015C1}.Release|Windows Mobile 6 Standard SDK (ARMV4I).Build.0 = Release|Windows Mobile 6 Standard SDK (ARMV4I)
+		{B82CDD25-6903-430E-BD38-D8129A2015C1}.Release|Windows Mobile 6 Standard SDK (ARMV4I).Deploy.0 = Release|Windows Mobile 6 Standard SDK (ARMV4I)
+		{B82CDD25-6903-430E-BD38-D8129A2015C1}.Release|x64.ActiveCfg = Release|x64
+		{B82CDD25-6903-430E-BD38-D8129A2015C1}.Release|x64.Build.0 = Release|x64
+		{B82CDD25-6903-430E-BD38-D8129A2015C1}.Release-Dynamic|Pocket PC 2003 (ARMV4).ActiveCfg = Release-Dynamic|Pocket PC 2003 (ARMV4)
+		{B82CDD25-6903-430E-BD38-D8129A2015C1}.Release-Dynamic|Pocket PC 2003 (ARMV4).Build.0 = Release-Dynamic|Pocket PC 2003 (ARMV4)
+		{B82CDD25-6903-430E-BD38-D8129A2015C1}.Release-Dynamic|Pocket PC 2003 (ARMV4).Deploy.0 = Release-Dynamic|Pocket PC 2003 (ARMV4)
+		{B82CDD25-6903-430E-BD38-D8129A2015C1}.Release-Dynamic|Smartphone 2003 (ARMV4).ActiveCfg = Release-Dynamic|Smartphone 2003 (ARMV4)
+		{B82CDD25-6903-430E-BD38-D8129A2015C1}.Release-Dynamic|Smartphone 2003 (ARMV4).Build.0 = Release-Dynamic|Smartphone 2003 (ARMV4)
+		{B82CDD25-6903-430E-BD38-D8129A2015C1}.Release-Dynamic|Smartphone 2003 (ARMV4).Deploy.0 = Release-Dynamic|Smartphone 2003 (ARMV4)
+		{B82CDD25-6903-430E-BD38-D8129A2015C1}.Release-Dynamic|Win32.ActiveCfg = Release-Dynamic|Win32
+		{B82CDD25-6903-430E-BD38-D8129A2015C1}.Release-Dynamic|Win32.Build.0 = Release-Dynamic|Win32
+		{B82CDD25-6903-430E-BD38-D8129A2015C1}.Release-Dynamic|Windows Mobile 5.0 Pocket PC SDK (ARMV4I).ActiveCfg = Release-Dynamic|Windows Mobile 6 Professional SDK (ARMV4I)
+		{B82CDD25-6903-430E-BD38-D8129A2015C1}.Release-Dynamic|Windows Mobile 5.0 Smartphone SDK (ARMV4I).ActiveCfg = Release-Dynamic|Windows Mobile 6 Professional SDK (ARMV4I)
+		{B82CDD25-6903-430E-BD38-D8129A2015C1}.Release-Dynamic|Windows Mobile 6 Professional SDK (ARMV4I).ActiveCfg = Release-Dynamic|Windows Mobile 6 Professional SDK (ARMV4I)
+		{B82CDD25-6903-430E-BD38-D8129A2015C1}.Release-Dynamic|Windows Mobile 6 Professional SDK (ARMV4I).Build.0 = Release-Dynamic|Windows Mobile 6 Professional SDK (ARMV4I)
+		{B82CDD25-6903-430E-BD38-D8129A2015C1}.Release-Dynamic|Windows Mobile 6 Professional SDK (ARMV4I).Deploy.0 = Release-Dynamic|Windows Mobile 6 Professional SDK (ARMV4I)
+		{B82CDD25-6903-430E-BD38-D8129A2015C1}.Release-Dynamic|Windows Mobile 6 Standard SDK (ARMV4I).ActiveCfg = Release-Dynamic|Windows Mobile 6 Standard SDK (ARMV4I)
+		{B82CDD25-6903-430E-BD38-D8129A2015C1}.Release-Dynamic|Windows Mobile 6 Standard SDK (ARMV4I).Build.0 = Release-Dynamic|Windows Mobile 6 Standard SDK (ARMV4I)
+		{B82CDD25-6903-430E-BD38-D8129A2015C1}.Release-Dynamic|Windows Mobile 6 Standard SDK (ARMV4I).Deploy.0 = Release-Dynamic|Windows Mobile 6 Standard SDK (ARMV4I)
+		{B82CDD25-6903-430E-BD38-D8129A2015C1}.Release-Dynamic|x64.ActiveCfg = Release-Dynamic|x64
+		{B82CDD25-6903-430E-BD38-D8129A2015C1}.Release-Dynamic|x64.Build.0 = Release-Dynamic|x64
+		{B82CDD25-6903-430E-BD38-D8129A2015C1}.Release-Static|Pocket PC 2003 (ARMV4).ActiveCfg = Release-Static|Pocket PC 2003 (ARMV4)
+		{B82CDD25-6903-430E-BD38-D8129A2015C1}.Release-Static|Pocket PC 2003 (ARMV4).Build.0 = Release-Static|Pocket PC 2003 (ARMV4)
+		{B82CDD25-6903-430E-BD38-D8129A2015C1}.Release-Static|Pocket PC 2003 (ARMV4).Deploy.0 = Release-Static|Pocket PC 2003 (ARMV4)
+		{B82CDD25-6903-430E-BD38-D8129A2015C1}.Release-Static|Smartphone 2003 (ARMV4).ActiveCfg = Release-Static|Smartphone 2003 (ARMV4)
+		{B82CDD25-6903-430E-BD38-D8129A2015C1}.Release-Static|Smartphone 2003 (ARMV4).Build.0 = Release-Static|Smartphone 2003 (ARMV4)
+		{B82CDD25-6903-430E-BD38-D8129A2015C1}.Release-Static|Smartphone 2003 (ARMV4).Deploy.0 = Release-Static|Smartphone 2003 (ARMV4)
+		{B82CDD25-6903-430E-BD38-D8129A2015C1}.Release-Static|Win32.ActiveCfg = Release-Static|Win32
+		{B82CDD25-6903-430E-BD38-D8129A2015C1}.Release-Static|Win32.Build.0 = Release-Static|Win32
+		{B82CDD25-6903-430E-BD38-D8129A2015C1}.Release-Static|Windows Mobile 5.0 Pocket PC SDK (ARMV4I).ActiveCfg = Release-Static|Windows Mobile 6 Professional SDK (ARMV4I)
+		{B82CDD25-6903-430E-BD38-D8129A2015C1}.Release-Static|Windows Mobile 5.0 Smartphone SDK (ARMV4I).ActiveCfg = Release-Static|Windows Mobile 6 Professional SDK (ARMV4I)
+		{B82CDD25-6903-430E-BD38-D8129A2015C1}.Release-Static|Windows Mobile 6 Professional SDK (ARMV4I).ActiveCfg = Release-Static|Windows Mobile 6 Professional SDK (ARMV4I)
+		{B82CDD25-6903-430E-BD38-D8129A2015C1}.Release-Static|Windows Mobile 6 Professional SDK (ARMV4I).Build.0 = Release-Static|Windows Mobile 6 Professional SDK (ARMV4I)
+		{B82CDD25-6903-430E-BD38-D8129A2015C1}.Release-Static|Windows Mobile 6 Professional SDK (ARMV4I).Deploy.0 = Release-Static|Windows Mobile 6 Professional SDK (ARMV4I)
+		{B82CDD25-6903-430E-BD38-D8129A2015C1}.Release-Static|Windows Mobile 6 Standard SDK (ARMV4I).ActiveCfg = Release-Static|Windows Mobile 6 Standard SDK (ARMV4I)
+		{B82CDD25-6903-430E-BD38-D8129A2015C1}.Release-Static|Windows Mobile 6 Standard SDK (ARMV4I).Build.0 = Release-Static|Windows Mobile 6 Standard SDK (ARMV4I)
+		{B82CDD25-6903-430E-BD38-D8129A2015C1}.Release-Static|Windows Mobile 6 Standard SDK (ARMV4I).Deploy.0 = Release-Static|Windows Mobile 6 Standard SDK (ARMV4I)
+		{B82CDD25-6903-430E-BD38-D8129A2015C1}.Release-Static|x64.ActiveCfg = Release-Static|x64
+		{B82CDD25-6903-430E-BD38-D8129A2015C1}.Release-Static|x64.Build.0 = Release-Static|x64
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
Index: /pjproject/trunk/pjsip-apps/build/Makefile
===================================================================
--- /pjproject/trunk/pjsip-apps/build/Makefile	(revision 4703)
+++ /pjproject/trunk/pjsip-apps/build/Makefile	(revision 4704)
@@ -63,8 +63,16 @@
 #
 #
+#  x   x  x  x  x  x  x   x  x  x  x  x  x   x  x  x  x  x  x   x  x  x  x  x
+#
+#                FIX THIS
+#
+#  x   x  x  x  x  x  x   x  x  x  x  x  x   x  x  x  x  x  x   x  x  x  x  x
 TARGETS := $(BINDIR)/$(PJSUA_EXE) $(BINDIR)/$(PJSYSTEST_EXE) samples
 
 all: $(TARGETS)
 
+swig:
+	$(MAKE) -C ../src/swig
+	
 doc:
 
Index: /pjproject/trunk/pjsip-apps/build/Samples.mak
===================================================================
--- /pjproject/trunk/pjsip-apps/build/Samples.mak	(revision 4703)
+++ /pjproject/trunk/pjsip-apps/build/Samples.mak	(revision 4704)
@@ -49,10 +49,18 @@
 EXES := $(foreach file, $(SAMPLES), $(file)$(HOST_EXE))
 
+PJSUA2_SAMPLES := pjsua2_demo
+
+PJSUA2_EXES := $(foreach file, $(PJSUA2_SAMPLES), $(file)$(HOST_EXE))
+
 .PHONY: $(EXES)
+.PHONY: $(PJSUA2_EXES)
 
-all: $(EXES)
+all: $(EXES) $(PJSUA2_EXES)
 
 $(EXES):
 	$(MAKE) --no-print-directory -f $(RULES_MAK) SAMPLE_SRCDIR=$(SRCDIR) SAMPLE_OBJS=$@.o SAMPLE_CFLAGS="$(_CFLAGS)" SAMPLE_LDFLAGS="$(_LDFLAGS)" SAMPLE_EXE=$@ APP=SAMPLE app=sample $(subst /,$(HOST_PSEP),$(BINDIR)/$@)
+
+$(PJSUA2_EXES):
+	$(MAKE) --no-print-directory -f $(RULES_MAK) SAMPLE_SRCDIR=$(SRCDIR) SAMPLE_OBJS=$@.o SAMPLE_CFLAGS="$(_CFLAGS)" SAMPLE_LDFLAGS="$(_LDFLAGS) -lstdc++" SAMPLE_EXE=$@ APP=SAMPLE app=sample $(subst /,$(HOST_PSEP),$(BINDIR)/$@)
 
 depend:
Index: /pjproject/trunk/pjsip-apps/build/sample_debug.vcproj
===================================================================
--- /pjproject/trunk/pjsip-apps/build/sample_debug.vcproj	(revision 4703)
+++ /pjproject/trunk/pjsip-apps/build/sample_debug.vcproj	(revision 4704)
@@ -312,76 +312,7 @@
 		</Configuration>
 		<Configuration
-			Name="Release|Win32"
-			ConfigurationType="1"
-			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-win32-release-defaults.vsprops"
-			UseOfMFC="0"
-			ATLMinimizesCRunTimeLibraryUsage="false"
-			CharacterSet="2"
-			>
-			<Tool
-				Name="VCPreBuildEventTool"
-			/>
-			<Tool
-				Name="VCCustomBuildTool"
-			/>
-			<Tool
-				Name="VCXMLDataGeneratorTool"
-			/>
-			<Tool
-				Name="VCWebServiceProxyGeneratorTool"
-			/>
-			<Tool
-				Name="VCMIDLTool"
-			/>
-			<Tool
-				Name="VCCLCompilerTool"
-				AdditionalIncludeDirectories="../../pjsip/include,../../pjlib/include,../../pjlib-util/include,../../pjmedia/include,../../pjnath/include"
-				PreprocessorDefinitions="_CONSOLE;"
-				PrecompiledHeaderFile=""
-			/>
-			<Tool
-				Name="VCManagedResourceCompilerTool"
-			/>
-			<Tool
-				Name="VCResourceCompilerTool"
-			/>
-			<Tool
-				Name="VCPreLinkEventTool"
-			/>
-			<Tool
-				Name="VCLinkerTool"
-				AdditionalDependencies="iphlpapi.lib dsound.lib dxguid.lib netapi32.lib mswsock.lib ws2_32.lib odbc32.lib odbccp32.lib oleaut32.lib uuid.lib ole32.lib user32.lib"
-				OutputFile="..\bin\sample-debug-$(TargetCPU)-$(PlatformName)-vc$(VSVer)-$(ConfigurationName).exe"
-				IgnoreDefaultLibraryNames=""
-			/>
-			<Tool
-				Name="VCALinkTool"
-			/>
-			<Tool
-				Name="VCManifestTool"
-			/>
-			<Tool
-				Name="VCXDCMakeTool"
-			/>
-			<Tool
-				Name="VCBscMakeTool"
-			/>
-			<Tool
-				Name="VCFxCopTool"
-			/>
-			<Tool
-				Name="VCAppVerifierTool"
-			/>
-			<Tool
-				Name="VCWebDeploymentTool"
-			/>
-			<Tool
-				Name="VCPostBuildEventTool"
-			/>
-		</Configuration>
-		<Configuration
-			Name="Release|Pocket PC 2003 (ARMV4)"
-			ConfigurationType="1"
-			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-release-defaults.vsprops"
+			Name="Debug|Windows Mobile 6 Standard SDK (ARMV4I)"
+			ConfigurationType="1"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-common-defaults.vsprops"
 			UseOfMFC="0"
 			ATLMinimizesCRunTimeLibraryUsage="false"
@@ -421,5 +352,5 @@
 				Name="VCLinkerTool"
 				AdditionalDependencies="iphlpapi.lib dsound.lib dxguid.lib netapi32.lib mswsock.lib ws2_32.lib odbc32.lib odbccp32.lib oleaut32.lib uuid.lib ole32.lib user32.lib"
-				OutputFile="..\bin\sample-debug-$(TargetCPU)-wm2003ppc-vc$(VSVer)-$(ConfigurationName).exe"
+				OutputFile="..\bin\sample-debug-$(TargetCPU)-wm6std-vc$(VSVer)-$(ConfigurationName).exe"
 			/>
 			<Tool
@@ -448,7 +379,7 @@
 		</Configuration>
 		<Configuration
-			Name="Release|Smartphone 2003 (ARMV4)"
-			ConfigurationType="1"
-			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-release-defaults.vsprops"
+			Name="Debug|Windows Mobile 6 Professional SDK (ARMV4I)"
+			ConfigurationType="1"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-common-defaults.vsprops"
 			UseOfMFC="0"
 			ATLMinimizesCRunTimeLibraryUsage="false"
@@ -488,5 +419,5 @@
 				Name="VCLinkerTool"
 				AdditionalDependencies="iphlpapi.lib dsound.lib dxguid.lib netapi32.lib mswsock.lib ws2_32.lib odbc32.lib odbccp32.lib oleaut32.lib uuid.lib ole32.lib user32.lib"
-				OutputFile="..\bin\sample-debug-$(TargetCPU)-wm2003sp-vc$(VSVer)-$(ConfigurationName).exe"
+				OutputFile="..\bin\sample-debug-$(TargetCPU)-wm6pro-vc$(VSVer)-$(ConfigurationName).exe"
 			/>
 			<Tool
@@ -515,7 +446,7 @@
 		</Configuration>
 		<Configuration
-			Name="Release|x64"
-			ConfigurationType="1"
-			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-win64-release-defaults.vsprops"
+			Name="Release|Win32"
+			ConfigurationType="1"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-win32-release-defaults.vsprops"
 			UseOfMFC="0"
 			ATLMinimizesCRunTimeLibraryUsage="false"
@@ -536,5 +467,4 @@
 			<Tool
 				Name="VCMIDLTool"
-				TargetEnvironment="3"
 			/>
 			<Tool
@@ -558,5 +488,4 @@
 				OutputFile="..\bin\sample-debug-$(TargetCPU)-$(PlatformName)-vc$(VSVer)-$(ConfigurationName).exe"
 				IgnoreDefaultLibraryNames=""
-				TargetMachine="17"
 			/>
 			<Tool
@@ -586,76 +515,7 @@
 		</Configuration>
 		<Configuration
-			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"
-			ATLMinimizesCRunTimeLibraryUsage="false"
-			CharacterSet="2"
-			>
-			<Tool
-				Name="VCPreBuildEventTool"
-			/>
-			<Tool
-				Name="VCCustomBuildTool"
-			/>
-			<Tool
-				Name="VCXMLDataGeneratorTool"
-			/>
-			<Tool
-				Name="VCWebServiceProxyGeneratorTool"
-			/>
-			<Tool
-				Name="VCMIDLTool"
-			/>
-			<Tool
-				Name="VCCLCompilerTool"
-				AdditionalIncludeDirectories="../../pjsip/include,../../pjlib/include,../../pjlib-util/include,../../pjmedia/include,../../pjnath/include"
-				PreprocessorDefinitions="_CONSOLE;"
-				PrecompiledHeaderFile=""
-			/>
-			<Tool
-				Name="VCManagedResourceCompilerTool"
-			/>
-			<Tool
-				Name="VCResourceCompilerTool"
-			/>
-			<Tool
-				Name="VCPreLinkEventTool"
-			/>
-			<Tool
-				Name="VCLinkerTool"
-				AdditionalDependencies="iphlpapi.lib dsound.lib dxguid.lib netapi32.lib mswsock.lib ws2_32.lib odbc32.lib odbccp32.lib oleaut32.lib uuid.lib ole32.lib user32.lib"
-				OutputFile="..\bin\sample-debug-$(TargetCPU)-$(PlatformName)-vc$(VSVer)-$(ConfigurationName).exe"
-				IgnoreDefaultLibraryNames="MSVCRT.LIB"
-			/>
-			<Tool
-				Name="VCALinkTool"
-			/>
-			<Tool
-				Name="VCManifestTool"
-			/>
-			<Tool
-				Name="VCXDCMakeTool"
-			/>
-			<Tool
-				Name="VCBscMakeTool"
-			/>
-			<Tool
-				Name="VCFxCopTool"
-			/>
-			<Tool
-				Name="VCAppVerifierTool"
-			/>
-			<Tool
-				Name="VCWebDeploymentTool"
-			/>
-			<Tool
-				Name="VCPostBuildEventTool"
-			/>
-		</Configuration>
-		<Configuration
-			Name="Debug-Static|Pocket PC 2003 (ARMV4)"
-			ConfigurationType="1"
-			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-common-defaults.vsprops"
+			Name="Release|Pocket PC 2003 (ARMV4)"
+			ConfigurationType="1"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-release-defaults.vsprops"
 			UseOfMFC="0"
 			ATLMinimizesCRunTimeLibraryUsage="false"
@@ -722,7 +582,7 @@
 		</Configuration>
 		<Configuration
-			Name="Debug-Static|Smartphone 2003 (ARMV4)"
-			ConfigurationType="1"
-			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-common-defaults.vsprops"
+			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"
 			ATLMinimizesCRunTimeLibraryUsage="false"
@@ -789,7 +649,7 @@
 		</Configuration>
 		<Configuration
-			Name="Debug-Static|x64"
-			ConfigurationType="1"
-			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-win64-common-defaults.vsprops"
+			Name="Release|x64"
+			ConfigurationType="1"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-win64-release-defaults.vsprops"
 			UseOfMFC="0"
 			ATLMinimizesCRunTimeLibraryUsage="false"
@@ -811,75 +671,4 @@
 				Name="VCMIDLTool"
 				TargetEnvironment="3"
-			/>
-			<Tool
-				Name="VCCLCompilerTool"
-				AdditionalIncludeDirectories="../../pjsip/include,../../pjlib/include,../../pjlib-util/include,../../pjmedia/include,../../pjnath/include"
-				PreprocessorDefinitions="_CONSOLE;"
-				PrecompiledHeaderFile=""
-				DebugInformationFormat="3"
-			/>
-			<Tool
-				Name="VCManagedResourceCompilerTool"
-			/>
-			<Tool
-				Name="VCResourceCompilerTool"
-			/>
-			<Tool
-				Name="VCPreLinkEventTool"
-			/>
-			<Tool
-				Name="VCLinkerTool"
-				AdditionalDependencies="iphlpapi.lib dsound.lib dxguid.lib netapi32.lib mswsock.lib ws2_32.lib odbc32.lib odbccp32.lib oleaut32.lib uuid.lib ole32.lib user32.lib"
-				OutputFile="..\bin\sample-debug-$(TargetCPU)-$(PlatformName)-vc$(VSVer)-$(ConfigurationName).exe"
-				IgnoreDefaultLibraryNames="MSVCRT.LIB"
-				TargetMachine="17"
-			/>
-			<Tool
-				Name="VCALinkTool"
-			/>
-			<Tool
-				Name="VCManifestTool"
-			/>
-			<Tool
-				Name="VCXDCMakeTool"
-			/>
-			<Tool
-				Name="VCBscMakeTool"
-			/>
-			<Tool
-				Name="VCFxCopTool"
-			/>
-			<Tool
-				Name="VCAppVerifierTool"
-			/>
-			<Tool
-				Name="VCWebDeploymentTool"
-			/>
-			<Tool
-				Name="VCPostBuildEventTool"
-			/>
-		</Configuration>
-		<Configuration
-			Name="Release-Dynamic|Win32"
-			ConfigurationType="1"
-			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-win32-release-defaults.vsprops"
-			UseOfMFC="0"
-			ATLMinimizesCRunTimeLibraryUsage="false"
-			CharacterSet="2"
-			>
-			<Tool
-				Name="VCPreBuildEventTool"
-			/>
-			<Tool
-				Name="VCCustomBuildTool"
-			/>
-			<Tool
-				Name="VCXMLDataGeneratorTool"
-			/>
-			<Tool
-				Name="VCWebServiceProxyGeneratorTool"
-			/>
-			<Tool
-				Name="VCMIDLTool"
 			/>
 			<Tool
@@ -903,4 +692,5 @@
 				OutputFile="..\bin\sample-debug-$(TargetCPU)-$(PlatformName)-vc$(VSVer)-$(ConfigurationName).exe"
 				IgnoreDefaultLibraryNames=""
+				TargetMachine="17"
 			/>
 			<Tool
@@ -930,7 +720,7 @@
 		</Configuration>
 		<Configuration
-			Name="Release-Dynamic|Pocket PC 2003 (ARMV4)"
-			ConfigurationType="1"
-			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-release-defaults.vsprops"
+			Name="Release|Windows Mobile 6 Standard SDK (ARMV4I)"
+			ConfigurationType="1"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-release-defaults.vsprops"
 			UseOfMFC="0"
 			ATLMinimizesCRunTimeLibraryUsage="false"
@@ -970,5 +760,5 @@
 				Name="VCLinkerTool"
 				AdditionalDependencies="iphlpapi.lib dsound.lib dxguid.lib netapi32.lib mswsock.lib ws2_32.lib odbc32.lib odbccp32.lib oleaut32.lib uuid.lib ole32.lib user32.lib"
-				OutputFile="..\bin\sample-debug-$(TargetCPU)-wm2003ppc-vc$(VSVer)-$(ConfigurationName).exe"
+				OutputFile="..\bin\sample-debug-$(TargetCPU)-wm6std-vc$(VSVer)-$(ConfigurationName).exe"
 			/>
 			<Tool
@@ -997,7 +787,7 @@
 		</Configuration>
 		<Configuration
-			Name="Release-Dynamic|Smartphone 2003 (ARMV4)"
-			ConfigurationType="1"
-			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-release-defaults.vsprops"
+			Name="Release|Windows Mobile 6 Professional SDK (ARMV4I)"
+			ConfigurationType="1"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-release-defaults.vsprops"
 			UseOfMFC="0"
 			ATLMinimizesCRunTimeLibraryUsage="false"
@@ -1037,5 +827,5 @@
 				Name="VCLinkerTool"
 				AdditionalDependencies="iphlpapi.lib dsound.lib dxguid.lib netapi32.lib mswsock.lib ws2_32.lib odbc32.lib odbccp32.lib oleaut32.lib uuid.lib ole32.lib user32.lib"
-				OutputFile="..\bin\sample-debug-$(TargetCPU)-wm2003sp-vc$(VSVer)-$(ConfigurationName).exe"
+				OutputFile="..\bin\sample-debug-$(TargetCPU)-wm6pro-vc$(VSVer)-$(ConfigurationName).exe"
 			/>
 			<Tool
@@ -1064,78 +854,7 @@
 		</Configuration>
 		<Configuration
-			Name="Release-Dynamic|x64"
-			ConfigurationType="1"
-			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-win64-release-defaults.vsprops"
-			UseOfMFC="0"
-			ATLMinimizesCRunTimeLibraryUsage="false"
-			CharacterSet="2"
-			>
-			<Tool
-				Name="VCPreBuildEventTool"
-			/>
-			<Tool
-				Name="VCCustomBuildTool"
-			/>
-			<Tool
-				Name="VCXMLDataGeneratorTool"
-			/>
-			<Tool
-				Name="VCWebServiceProxyGeneratorTool"
-			/>
-			<Tool
-				Name="VCMIDLTool"
-				TargetEnvironment="3"
-			/>
-			<Tool
-				Name="VCCLCompilerTool"
-				AdditionalIncludeDirectories="../../pjsip/include,../../pjlib/include,../../pjlib-util/include,../../pjmedia/include,../../pjnath/include"
-				PreprocessorDefinitions="_CONSOLE;"
-				PrecompiledHeaderFile=""
-			/>
-			<Tool
-				Name="VCManagedResourceCompilerTool"
-			/>
-			<Tool
-				Name="VCResourceCompilerTool"
-			/>
-			<Tool
-				Name="VCPreLinkEventTool"
-			/>
-			<Tool
-				Name="VCLinkerTool"
-				AdditionalDependencies="iphlpapi.lib dsound.lib dxguid.lib netapi32.lib mswsock.lib ws2_32.lib odbc32.lib odbccp32.lib oleaut32.lib uuid.lib ole32.lib user32.lib"
-				OutputFile="..\bin\sample-debug-$(TargetCPU)-$(PlatformName)-vc$(VSVer)-$(ConfigurationName).exe"
-				IgnoreDefaultLibraryNames=""
-				TargetMachine="17"
-			/>
-			<Tool
-				Name="VCALinkTool"
-			/>
-			<Tool
-				Name="VCManifestTool"
-			/>
-			<Tool
-				Name="VCXDCMakeTool"
-			/>
-			<Tool
-				Name="VCBscMakeTool"
-			/>
-			<Tool
-				Name="VCFxCopTool"
-			/>
-			<Tool
-				Name="VCAppVerifierTool"
-			/>
-			<Tool
-				Name="VCWebDeploymentTool"
-			/>
-			<Tool
-				Name="VCPostBuildEventTool"
-			/>
-		</Configuration>
-		<Configuration
-			Name="Debug-Dynamic|Win32"
-			ConfigurationType="1"
-			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-win32-common-defaults.vsprops"
+			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"
 			ATLMinimizesCRunTimeLibraryUsage="false"
@@ -1204,7 +923,7 @@
 		</Configuration>
 		<Configuration
-			Name="Debug-Dynamic|Pocket PC 2003 (ARMV4)"
-			ConfigurationType="1"
-			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-common-defaults.vsprops"
+			Name="Debug-Static|Pocket PC 2003 (ARMV4)"
+			ConfigurationType="1"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-common-defaults.vsprops"
 			UseOfMFC="0"
 			ATLMinimizesCRunTimeLibraryUsage="false"
@@ -1271,7 +990,7 @@
 		</Configuration>
 		<Configuration
-			Name="Debug-Dynamic|Smartphone 2003 (ARMV4)"
-			ConfigurationType="1"
-			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-common-defaults.vsprops"
+			Name="Debug-Static|Smartphone 2003 (ARMV4)"
+			ConfigurationType="1"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-common-defaults.vsprops"
 			UseOfMFC="0"
 			ATLMinimizesCRunTimeLibraryUsage="false"
@@ -1338,7 +1057,7 @@
 		</Configuration>
 		<Configuration
-			Name="Debug-Dynamic|x64"
-			ConfigurationType="1"
-			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-win64-common-defaults.vsprops"
+			Name="Debug-Static|x64"
+			ConfigurationType="1"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-win64-common-defaults.vsprops"
 			UseOfMFC="0"
 			ATLMinimizesCRunTimeLibraryUsage="false"
@@ -1410,76 +1129,7 @@
 		</Configuration>
 		<Configuration
-			Name="Release-Static|Win32"
-			ConfigurationType="1"
-			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-win32-release-defaults.vsprops"
-			UseOfMFC="0"
-			ATLMinimizesCRunTimeLibraryUsage="false"
-			CharacterSet="2"
-			>
-			<Tool
-				Name="VCPreBuildEventTool"
-			/>
-			<Tool
-				Name="VCCustomBuildTool"
-			/>
-			<Tool
-				Name="VCXMLDataGeneratorTool"
-			/>
-			<Tool
-				Name="VCWebServiceProxyGeneratorTool"
-			/>
-			<Tool
-				Name="VCMIDLTool"
-			/>
-			<Tool
-				Name="VCCLCompilerTool"
-				AdditionalIncludeDirectories="../../pjsip/include,../../pjlib/include,../../pjlib-util/include,../../pjmedia/include,../../pjnath/include"
-				PreprocessorDefinitions="_CONSOLE;"
-				PrecompiledHeaderFile=""
-			/>
-			<Tool
-				Name="VCManagedResourceCompilerTool"
-			/>
-			<Tool
-				Name="VCResourceCompilerTool"
-			/>
-			<Tool
-				Name="VCPreLinkEventTool"
-			/>
-			<Tool
-				Name="VCLinkerTool"
-				AdditionalDependencies="iphlpapi.lib dsound.lib dxguid.lib netapi32.lib mswsock.lib ws2_32.lib odbc32.lib odbccp32.lib oleaut32.lib uuid.lib ole32.lib user32.lib"
-				OutputFile="..\bin\sample-debug-$(TargetCPU)-$(PlatformName)-vc$(VSVer)-$(ConfigurationName).exe"
-				IgnoreDefaultLibraryNames="MSVCRT.LIB"
-			/>
-			<Tool
-				Name="VCALinkTool"
-			/>
-			<Tool
-				Name="VCManifestTool"
-			/>
-			<Tool
-				Name="VCXDCMakeTool"
-			/>
-			<Tool
-				Name="VCBscMakeTool"
-			/>
-			<Tool
-				Name="VCFxCopTool"
-			/>
-			<Tool
-				Name="VCAppVerifierTool"
-			/>
-			<Tool
-				Name="VCWebDeploymentTool"
-			/>
-			<Tool
-				Name="VCPostBuildEventTool"
-			/>
-		</Configuration>
-		<Configuration
-			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"
+			Name="Debug-Static|Windows Mobile 6 Standard SDK (ARMV4I)"
+			ConfigurationType="1"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-common-defaults.vsprops"
 			UseOfMFC="0"
 			ATLMinimizesCRunTimeLibraryUsage="false"
@@ -1519,5 +1169,5 @@
 				Name="VCLinkerTool"
 				AdditionalDependencies="iphlpapi.lib dsound.lib dxguid.lib netapi32.lib mswsock.lib ws2_32.lib odbc32.lib odbccp32.lib oleaut32.lib uuid.lib ole32.lib user32.lib"
-				OutputFile="..\bin\sample-debug-$(TargetCPU)-wm2003ppc-vc$(VSVer)-$(ConfigurationName).exe"
+				OutputFile="..\bin\sample-debug-$(TargetCPU)-wm6std-vc$(VSVer)-$(ConfigurationName).exe"
 			/>
 			<Tool
@@ -1546,7 +1196,7 @@
 		</Configuration>
 		<Configuration
-			Name="Release-Static|Smartphone 2003 (ARMV4)"
-			ConfigurationType="1"
-			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-release-defaults.vsprops"
+			Name="Debug-Static|Windows Mobile 6 Professional SDK (ARMV4I)"
+			ConfigurationType="1"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-common-defaults.vsprops"
 			UseOfMFC="0"
 			ATLMinimizesCRunTimeLibraryUsage="false"
@@ -1586,5 +1236,5 @@
 				Name="VCLinkerTool"
 				AdditionalDependencies="iphlpapi.lib dsound.lib dxguid.lib netapi32.lib mswsock.lib ws2_32.lib odbc32.lib odbccp32.lib oleaut32.lib uuid.lib ole32.lib user32.lib"
-				OutputFile="..\bin\sample-debug-$(TargetCPU)-wm2003sp-vc$(VSVer)-$(ConfigurationName).exe"
+				OutputFile="..\bin\sample-debug-$(TargetCPU)-wm6pro-vc$(VSVer)-$(ConfigurationName).exe"
 			/>
 			<Tool
@@ -1613,7 +1263,7 @@
 		</Configuration>
 		<Configuration
-			Name="Release-Static|x64"
-			ConfigurationType="1"
-			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-win64-release-defaults.vsprops"
+			Name="Release-Dynamic|Win32"
+			ConfigurationType="1"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-win32-release-defaults.vsprops"
 			UseOfMFC="0"
 			ATLMinimizesCRunTimeLibraryUsage="false"
@@ -1634,5 +1284,4 @@
 			<Tool
 				Name="VCMIDLTool"
-				TargetEnvironment="3"
 			/>
 			<Tool
@@ -1655,6 +1304,5 @@
 				AdditionalDependencies="iphlpapi.lib dsound.lib dxguid.lib netapi32.lib mswsock.lib ws2_32.lib odbc32.lib odbccp32.lib oleaut32.lib uuid.lib ole32.lib user32.lib"
 				OutputFile="..\bin\sample-debug-$(TargetCPU)-$(PlatformName)-vc$(VSVer)-$(ConfigurationName).exe"
-				IgnoreDefaultLibraryNames="MSVCRT.LIB"
-				TargetMachine="17"
+				IgnoreDefaultLibraryNames=""
 			/>
 			<Tool
@@ -1684,7 +1332,7 @@
 		</Configuration>
 		<Configuration
-			Name="Debug|Windows Mobile 6 Standard SDK (ARMV4I)"
-			ConfigurationType="1"
-			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-common-defaults.vsprops"
+			Name="Release-Dynamic|Pocket PC 2003 (ARMV4)"
+			ConfigurationType="1"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-release-defaults.vsprops"
 			UseOfMFC="0"
 			ATLMinimizesCRunTimeLibraryUsage="false"
@@ -1724,5 +1372,5 @@
 				Name="VCLinkerTool"
 				AdditionalDependencies="iphlpapi.lib dsound.lib dxguid.lib netapi32.lib mswsock.lib ws2_32.lib odbc32.lib odbccp32.lib oleaut32.lib uuid.lib ole32.lib user32.lib"
-				OutputFile="..\bin\sample-debug-$(TargetCPU)-wm6std-vc$(VSVer)-$(ConfigurationName).exe"
+				OutputFile="..\bin\sample-debug-$(TargetCPU)-wm2003ppc-vc$(VSVer)-$(ConfigurationName).exe"
 			/>
 			<Tool
@@ -1751,7 +1399,7 @@
 		</Configuration>
 		<Configuration
-			Name="Debug|Windows Mobile 6 Professional SDK (ARMV4I)"
-			ConfigurationType="1"
-			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-common-defaults.vsprops"
+			Name="Release-Dynamic|Smartphone 2003 (ARMV4)"
+			ConfigurationType="1"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-release-defaults.vsprops"
 			UseOfMFC="0"
 			ATLMinimizesCRunTimeLibraryUsage="false"
@@ -1791,5 +1439,5 @@
 				Name="VCLinkerTool"
 				AdditionalDependencies="iphlpapi.lib dsound.lib dxguid.lib netapi32.lib mswsock.lib ws2_32.lib odbc32.lib odbccp32.lib oleaut32.lib uuid.lib ole32.lib user32.lib"
-				OutputFile="..\bin\sample-debug-$(TargetCPU)-wm6pro-vc$(VSVer)-$(ConfigurationName).exe"
+				OutputFile="..\bin\sample-debug-$(TargetCPU)-wm2003sp-vc$(VSVer)-$(ConfigurationName).exe"
 			/>
 			<Tool
@@ -1818,7 +1466,78 @@
 		</Configuration>
 		<Configuration
-			Name="Debug|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)"
-			ConfigurationType="1"
-			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm5-common-defaults.vsprops"
+			Name="Release-Dynamic|x64"
+			ConfigurationType="1"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-win64-release-defaults.vsprops"
+			UseOfMFC="0"
+			ATLMinimizesCRunTimeLibraryUsage="false"
+			CharacterSet="2"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+				TargetEnvironment="3"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				AdditionalIncludeDirectories="../../pjsip/include,../../pjlib/include,../../pjlib-util/include,../../pjmedia/include,../../pjnath/include"
+				PreprocessorDefinitions="_CONSOLE;"
+				PrecompiledHeaderFile=""
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				AdditionalDependencies="iphlpapi.lib dsound.lib dxguid.lib netapi32.lib mswsock.lib ws2_32.lib odbc32.lib odbccp32.lib oleaut32.lib uuid.lib ole32.lib user32.lib"
+				OutputFile="..\bin\sample-debug-$(TargetCPU)-$(PlatformName)-vc$(VSVer)-$(ConfigurationName).exe"
+				IgnoreDefaultLibraryNames=""
+				TargetMachine="17"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCWebDeploymentTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release-Dynamic|Windows Mobile 6 Standard SDK (ARMV4I)"
+			ConfigurationType="1"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-release-defaults.vsprops"
 			UseOfMFC="0"
 			ATLMinimizesCRunTimeLibraryUsage="false"
@@ -1858,5 +1577,5 @@
 				Name="VCLinkerTool"
 				AdditionalDependencies="iphlpapi.lib dsound.lib dxguid.lib netapi32.lib mswsock.lib ws2_32.lib odbc32.lib odbccp32.lib oleaut32.lib uuid.lib ole32.lib user32.lib"
-				OutputFile="..\bin\sample-debug-$(TargetCPU)-wm5ppc-vc$(VSVer)-$(ConfigurationName).exe"
+				OutputFile="..\bin\sample-debug-$(TargetCPU)-wm6std-vc$(VSVer)-$(ConfigurationName).exe"
 			/>
 			<Tool
@@ -1885,7 +1604,7 @@
 		</Configuration>
 		<Configuration
-			Name="Debug|Windows Mobile 5.0 Smartphone SDK (ARMV4I)"
-			ConfigurationType="1"
-			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm5-common-defaults.vsprops"
+			Name="Release-Dynamic|Windows Mobile 6 Professional SDK (ARMV4I)"
+			ConfigurationType="1"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-release-defaults.vsprops"
 			UseOfMFC="0"
 			ATLMinimizesCRunTimeLibraryUsage="false"
@@ -1925,5 +1644,5 @@
 				Name="VCLinkerTool"
 				AdditionalDependencies="iphlpapi.lib dsound.lib dxguid.lib netapi32.lib mswsock.lib ws2_32.lib odbc32.lib odbccp32.lib oleaut32.lib uuid.lib ole32.lib user32.lib"
-				OutputFile="..\bin\sample-debug-$(TargetCPU)-wm5sp-vc$(VSVer)-$(ConfigurationName).exe"
+				OutputFile="..\bin\sample-debug-$(TargetCPU)-wm6pro-vc$(VSVer)-$(ConfigurationName).exe"
 			/>
 			<Tool
@@ -1952,7 +1671,76 @@
 		</Configuration>
 		<Configuration
-			Name="Release|Windows Mobile 6 Standard SDK (ARMV4I)"
-			ConfigurationType="1"
-			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-release-defaults.vsprops"
+			Name="Debug-Dynamic|Win32"
+			ConfigurationType="1"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-win32-common-defaults.vsprops"
+			UseOfMFC="0"
+			ATLMinimizesCRunTimeLibraryUsage="false"
+			CharacterSet="2"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				AdditionalIncludeDirectories="../../pjsip/include,../../pjlib/include,../../pjlib-util/include,../../pjmedia/include,../../pjnath/include"
+				PreprocessorDefinitions="_CONSOLE;"
+				PrecompiledHeaderFile=""
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				AdditionalDependencies="iphlpapi.lib dsound.lib dxguid.lib netapi32.lib mswsock.lib ws2_32.lib odbc32.lib odbccp32.lib oleaut32.lib uuid.lib ole32.lib user32.lib"
+				OutputFile="..\bin\sample-debug-$(TargetCPU)-$(PlatformName)-vc$(VSVer)-$(ConfigurationName).exe"
+				IgnoreDefaultLibraryNames="MSVCRT.LIB"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCWebDeploymentTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Debug-Dynamic|Pocket PC 2003 (ARMV4)"
+			ConfigurationType="1"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-common-defaults.vsprops"
 			UseOfMFC="0"
 			ATLMinimizesCRunTimeLibraryUsage="false"
@@ -1992,5 +1780,5 @@
 				Name="VCLinkerTool"
 				AdditionalDependencies="iphlpapi.lib dsound.lib dxguid.lib netapi32.lib mswsock.lib ws2_32.lib odbc32.lib odbccp32.lib oleaut32.lib uuid.lib ole32.lib user32.lib"
-				OutputFile="..\bin\sample-debug-$(TargetCPU)-wm6std-vc$(VSVer)-$(ConfigurationName).exe"
+				OutputFile="..\bin\sample-debug-$(TargetCPU)-wm2003ppc-vc$(VSVer)-$(ConfigurationName).exe"
 			/>
 			<Tool
@@ -2019,7 +1807,7 @@
 		</Configuration>
 		<Configuration
-			Name="Release|Windows Mobile 6 Professional SDK (ARMV4I)"
-			ConfigurationType="1"
-			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-release-defaults.vsprops"
+			Name="Debug-Dynamic|Smartphone 2003 (ARMV4)"
+			ConfigurationType="1"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-common-defaults.vsprops"
 			UseOfMFC="0"
 			ATLMinimizesCRunTimeLibraryUsage="false"
@@ -2059,5 +1847,5 @@
 				Name="VCLinkerTool"
 				AdditionalDependencies="iphlpapi.lib dsound.lib dxguid.lib netapi32.lib mswsock.lib ws2_32.lib odbc32.lib odbccp32.lib oleaut32.lib uuid.lib ole32.lib user32.lib"
-				OutputFile="..\bin\sample-debug-$(TargetCPU)-wm6pro-vc$(VSVer)-$(ConfigurationName).exe"
+				OutputFile="..\bin\sample-debug-$(TargetCPU)-wm2003sp-vc$(VSVer)-$(ConfigurationName).exe"
 			/>
 			<Tool
@@ -2086,7 +1874,79 @@
 		</Configuration>
 		<Configuration
-			Name="Release|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)"
-			ConfigurationType="1"
-			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm5-release-defaults.vsprops"
+			Name="Debug-Dynamic|x64"
+			ConfigurationType="1"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-win64-common-defaults.vsprops"
+			UseOfMFC="0"
+			ATLMinimizesCRunTimeLibraryUsage="false"
+			CharacterSet="2"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+				TargetEnvironment="3"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				AdditionalIncludeDirectories="../../pjsip/include,../../pjlib/include,../../pjlib-util/include,../../pjmedia/include,../../pjnath/include"
+				PreprocessorDefinitions="_CONSOLE;"
+				PrecompiledHeaderFile=""
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				AdditionalDependencies="iphlpapi.lib dsound.lib dxguid.lib netapi32.lib mswsock.lib ws2_32.lib odbc32.lib odbccp32.lib oleaut32.lib uuid.lib ole32.lib user32.lib"
+				OutputFile="..\bin\sample-debug-$(TargetCPU)-$(PlatformName)-vc$(VSVer)-$(ConfigurationName).exe"
+				IgnoreDefaultLibraryNames="MSVCRT.LIB"
+				TargetMachine="17"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCWebDeploymentTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Debug-Dynamic|Windows Mobile 6 Standard SDK (ARMV4I)"
+			ConfigurationType="1"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-common-defaults.vsprops"
 			UseOfMFC="0"
 			ATLMinimizesCRunTimeLibraryUsage="false"
@@ -2126,5 +1986,5 @@
 				Name="VCLinkerTool"
 				AdditionalDependencies="iphlpapi.lib dsound.lib dxguid.lib netapi32.lib mswsock.lib ws2_32.lib odbc32.lib odbccp32.lib oleaut32.lib uuid.lib ole32.lib user32.lib"
-				OutputFile="..\bin\sample-debug-$(TargetCPU)-wm5ppc-vc$(VSVer)-$(ConfigurationName).exe"
+				OutputFile="..\bin\sample-debug-$(TargetCPU)-wm6std-vc$(VSVer)-$(ConfigurationName).exe"
 			/>
 			<Tool
@@ -2153,7 +2013,7 @@
 		</Configuration>
 		<Configuration
-			Name="Release|Windows Mobile 5.0 Smartphone SDK (ARMV4I)"
-			ConfigurationType="1"
-			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm5-release-defaults.vsprops"
+			Name="Debug-Dynamic|Windows Mobile 6 Professional SDK (ARMV4I)"
+			ConfigurationType="1"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-common-defaults.vsprops"
 			UseOfMFC="0"
 			ATLMinimizesCRunTimeLibraryUsage="false"
@@ -2193,5 +2053,5 @@
 				Name="VCLinkerTool"
 				AdditionalDependencies="iphlpapi.lib dsound.lib dxguid.lib netapi32.lib mswsock.lib ws2_32.lib odbc32.lib odbccp32.lib oleaut32.lib uuid.lib ole32.lib user32.lib"
-				OutputFile="..\bin\sample-debug-$(TargetCPU)-wm5sp-vc$(VSVer)-$(ConfigurationName).exe"
+				OutputFile="..\bin\sample-debug-$(TargetCPU)-wm6pro-vc$(VSVer)-$(ConfigurationName).exe"
 			/>
 			<Tool
@@ -2220,7 +2080,76 @@
 		</Configuration>
 		<Configuration
-			Name="Debug-Static|Windows Mobile 6 Standard SDK (ARMV4I)"
-			ConfigurationType="1"
-			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-common-defaults.vsprops"
+			Name="Release-Static|Win32"
+			ConfigurationType="1"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-win32-release-defaults.vsprops"
+			UseOfMFC="0"
+			ATLMinimizesCRunTimeLibraryUsage="false"
+			CharacterSet="2"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				AdditionalIncludeDirectories="../../pjsip/include,../../pjlib/include,../../pjlib-util/include,../../pjmedia/include,../../pjnath/include"
+				PreprocessorDefinitions="_CONSOLE;"
+				PrecompiledHeaderFile=""
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				AdditionalDependencies="iphlpapi.lib dsound.lib dxguid.lib netapi32.lib mswsock.lib ws2_32.lib odbc32.lib odbccp32.lib oleaut32.lib uuid.lib ole32.lib user32.lib"
+				OutputFile="..\bin\sample-debug-$(TargetCPU)-$(PlatformName)-vc$(VSVer)-$(ConfigurationName).exe"
+				IgnoreDefaultLibraryNames="MSVCRT.LIB"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCWebDeploymentTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			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"
 			ATLMinimizesCRunTimeLibraryUsage="false"
@@ -2260,5 +2189,5 @@
 				Name="VCLinkerTool"
 				AdditionalDependencies="iphlpapi.lib dsound.lib dxguid.lib netapi32.lib mswsock.lib ws2_32.lib odbc32.lib odbccp32.lib oleaut32.lib uuid.lib ole32.lib user32.lib"
-				OutputFile="..\bin\sample-debug-$(TargetCPU)-wm6std-vc$(VSVer)-$(ConfigurationName).exe"
+				OutputFile="..\bin\sample-debug-$(TargetCPU)-wm2003ppc-vc$(VSVer)-$(ConfigurationName).exe"
 			/>
 			<Tool
@@ -2287,7 +2216,7 @@
 		</Configuration>
 		<Configuration
-			Name="Debug-Static|Windows Mobile 6 Professional SDK (ARMV4I)"
-			ConfigurationType="1"
-			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-common-defaults.vsprops"
+			Name="Release-Static|Smartphone 2003 (ARMV4)"
+			ConfigurationType="1"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-release-defaults.vsprops"
 			UseOfMFC="0"
 			ATLMinimizesCRunTimeLibraryUsage="false"
@@ -2327,5 +2256,5 @@
 				Name="VCLinkerTool"
 				AdditionalDependencies="iphlpapi.lib dsound.lib dxguid.lib netapi32.lib mswsock.lib ws2_32.lib odbc32.lib odbccp32.lib oleaut32.lib uuid.lib ole32.lib user32.lib"
-				OutputFile="..\bin\sample-debug-$(TargetCPU)-wm6pro-vc$(VSVer)-$(ConfigurationName).exe"
+				OutputFile="..\bin\sample-debug-$(TargetCPU)-wm2003sp-vc$(VSVer)-$(ConfigurationName).exe"
 			/>
 			<Tool
@@ -2354,7 +2283,78 @@
 		</Configuration>
 		<Configuration
-			Name="Debug-Static|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)"
-			ConfigurationType="1"
-			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm5-common-defaults.vsprops"
+			Name="Release-Static|x64"
+			ConfigurationType="1"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-win64-release-defaults.vsprops"
+			UseOfMFC="0"
+			ATLMinimizesCRunTimeLibraryUsage="false"
+			CharacterSet="2"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+				TargetEnvironment="3"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				AdditionalIncludeDirectories="../../pjsip/include,../../pjlib/include,../../pjlib-util/include,../../pjmedia/include,../../pjnath/include"
+				PreprocessorDefinitions="_CONSOLE;"
+				PrecompiledHeaderFile=""
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				AdditionalDependencies="iphlpapi.lib dsound.lib dxguid.lib netapi32.lib mswsock.lib ws2_32.lib odbc32.lib odbccp32.lib oleaut32.lib uuid.lib ole32.lib user32.lib"
+				OutputFile="..\bin\sample-debug-$(TargetCPU)-$(PlatformName)-vc$(VSVer)-$(ConfigurationName).exe"
+				IgnoreDefaultLibraryNames="MSVCRT.LIB"
+				TargetMachine="17"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCWebDeploymentTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release-Static|Windows Mobile 6 Standard SDK (ARMV4I)"
+			ConfigurationType="1"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-release-defaults.vsprops"
 			UseOfMFC="0"
 			ATLMinimizesCRunTimeLibraryUsage="false"
@@ -2394,5 +2394,5 @@
 				Name="VCLinkerTool"
 				AdditionalDependencies="iphlpapi.lib dsound.lib dxguid.lib netapi32.lib mswsock.lib ws2_32.lib odbc32.lib odbccp32.lib oleaut32.lib uuid.lib ole32.lib user32.lib"
-				OutputFile="..\bin\sample-debug-$(TargetCPU)-wm5ppc-vc$(VSVer)-$(ConfigurationName).exe"
+				OutputFile="..\bin\sample-debug-$(TargetCPU)-wm6std-vc$(VSVer)-$(ConfigurationName).exe"
 			/>
 			<Tool
@@ -2421,7 +2421,7 @@
 		</Configuration>
 		<Configuration
-			Name="Debug-Static|Windows Mobile 5.0 Smartphone SDK (ARMV4I)"
-			ConfigurationType="1"
-			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm5-common-defaults.vsprops"
+			Name="Release-Static|Windows Mobile 6 Professional SDK (ARMV4I)"
+			ConfigurationType="1"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-release-defaults.vsprops"
 			UseOfMFC="0"
 			ATLMinimizesCRunTimeLibraryUsage="false"
@@ -2461,5 +2461,5 @@
 				Name="VCLinkerTool"
 				AdditionalDependencies="iphlpapi.lib dsound.lib dxguid.lib netapi32.lib mswsock.lib ws2_32.lib odbc32.lib odbccp32.lib oleaut32.lib uuid.lib ole32.lib user32.lib"
-				OutputFile="..\bin\sample-debug-$(TargetCPU)-wm5sp-vc$(VSVer)-$(ConfigurationName).exe"
+				OutputFile="..\bin\sample-debug-$(TargetCPU)-wm6pro-vc$(VSVer)-$(ConfigurationName).exe"
 			/>
 			<Tool
@@ -2488,7 +2488,7 @@
 		</Configuration>
 		<Configuration
-			Name="Release-Dynamic|Windows Mobile 6 Standard SDK (ARMV4I)"
-			ConfigurationType="1"
-			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-release-defaults.vsprops"
+			Name="Debug|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)"
+			ConfigurationType="1"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm5-common-defaults.vsprops"
 			UseOfMFC="0"
 			ATLMinimizesCRunTimeLibraryUsage="false"
@@ -2528,5 +2528,5 @@
 				Name="VCLinkerTool"
 				AdditionalDependencies="iphlpapi.lib dsound.lib dxguid.lib netapi32.lib mswsock.lib ws2_32.lib odbc32.lib odbccp32.lib oleaut32.lib uuid.lib ole32.lib user32.lib"
-				OutputFile="..\bin\sample-debug-$(TargetCPU)-wm6std-vc$(VSVer)-$(ConfigurationName).exe"
+				OutputFile="..\bin\sample-debug-$(TargetCPU)-wm5ppc-vc$(VSVer)-$(ConfigurationName).exe"
 			/>
 			<Tool
@@ -2555,7 +2555,7 @@
 		</Configuration>
 		<Configuration
-			Name="Release-Dynamic|Windows Mobile 6 Professional SDK (ARMV4I)"
-			ConfigurationType="1"
-			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-release-defaults.vsprops"
+			Name="Debug|Windows Mobile 5.0 Smartphone SDK (ARMV4I)"
+			ConfigurationType="1"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm5-common-defaults.vsprops"
 			UseOfMFC="0"
 			ATLMinimizesCRunTimeLibraryUsage="false"
@@ -2595,5 +2595,5 @@
 				Name="VCLinkerTool"
 				AdditionalDependencies="iphlpapi.lib dsound.lib dxguid.lib netapi32.lib mswsock.lib ws2_32.lib odbc32.lib odbccp32.lib oleaut32.lib uuid.lib ole32.lib user32.lib"
-				OutputFile="..\bin\sample-debug-$(TargetCPU)-wm6pro-vc$(VSVer)-$(ConfigurationName).exe"
+				OutputFile="..\bin\sample-debug-$(TargetCPU)-wm5sp-vc$(VSVer)-$(ConfigurationName).exe"
 			/>
 			<Tool
@@ -2622,5 +2622,5 @@
 		</Configuration>
 		<Configuration
-			Name="Release-Dynamic|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)"
+			Name="Release|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)"
 			ConfigurationType="1"
 			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm5-release-defaults.vsprops"
@@ -2689,5 +2689,5 @@
 		</Configuration>
 		<Configuration
-			Name="Release-Dynamic|Windows Mobile 5.0 Smartphone SDK (ARMV4I)"
+			Name="Release|Windows Mobile 5.0 Smartphone SDK (ARMV4I)"
 			ConfigurationType="1"
 			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm5-release-defaults.vsprops"
@@ -2756,7 +2756,7 @@
 		</Configuration>
 		<Configuration
-			Name="Debug-Dynamic|Windows Mobile 6 Standard SDK (ARMV4I)"
-			ConfigurationType="1"
-			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-common-defaults.vsprops"
+			Name="Debug-Static|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)"
+			ConfigurationType="1"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm5-common-defaults.vsprops"
 			UseOfMFC="0"
 			ATLMinimizesCRunTimeLibraryUsage="false"
@@ -2796,5 +2796,5 @@
 				Name="VCLinkerTool"
 				AdditionalDependencies="iphlpapi.lib dsound.lib dxguid.lib netapi32.lib mswsock.lib ws2_32.lib odbc32.lib odbccp32.lib oleaut32.lib uuid.lib ole32.lib user32.lib"
-				OutputFile="..\bin\sample-debug-$(TargetCPU)-wm6std-vc$(VSVer)-$(ConfigurationName).exe"
+				OutputFile="..\bin\sample-debug-$(TargetCPU)-wm5ppc-vc$(VSVer)-$(ConfigurationName).exe"
 			/>
 			<Tool
@@ -2823,7 +2823,7 @@
 		</Configuration>
 		<Configuration
-			Name="Debug-Dynamic|Windows Mobile 6 Professional SDK (ARMV4I)"
-			ConfigurationType="1"
-			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-common-defaults.vsprops"
+			Name="Debug-Static|Windows Mobile 5.0 Smartphone SDK (ARMV4I)"
+			ConfigurationType="1"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm5-common-defaults.vsprops"
 			UseOfMFC="0"
 			ATLMinimizesCRunTimeLibraryUsage="false"
@@ -2863,5 +2863,5 @@
 				Name="VCLinkerTool"
 				AdditionalDependencies="iphlpapi.lib dsound.lib dxguid.lib netapi32.lib mswsock.lib ws2_32.lib odbc32.lib odbccp32.lib oleaut32.lib uuid.lib ole32.lib user32.lib"
-				OutputFile="..\bin\sample-debug-$(TargetCPU)-wm6pro-vc$(VSVer)-$(ConfigurationName).exe"
+				OutputFile="..\bin\sample-debug-$(TargetCPU)-wm5sp-vc$(VSVer)-$(ConfigurationName).exe"
 			/>
 			<Tool
@@ -2890,7 +2890,7 @@
 		</Configuration>
 		<Configuration
-			Name="Debug-Dynamic|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)"
-			ConfigurationType="1"
-			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm5-common-defaults.vsprops"
+			Name="Release-Dynamic|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)"
+			ConfigurationType="1"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm5-release-defaults.vsprops"
 			UseOfMFC="0"
 			ATLMinimizesCRunTimeLibraryUsage="false"
@@ -2957,7 +2957,7 @@
 		</Configuration>
 		<Configuration
-			Name="Debug-Dynamic|Windows Mobile 5.0 Smartphone SDK (ARMV4I)"
-			ConfigurationType="1"
-			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm5-common-defaults.vsprops"
+			Name="Release-Dynamic|Windows Mobile 5.0 Smartphone SDK (ARMV4I)"
+			ConfigurationType="1"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm5-release-defaults.vsprops"
 			UseOfMFC="0"
 			ATLMinimizesCRunTimeLibraryUsage="false"
@@ -3024,7 +3024,7 @@
 		</Configuration>
 		<Configuration
-			Name="Release-Static|Windows Mobile 6 Standard SDK (ARMV4I)"
-			ConfigurationType="1"
-			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-release-defaults.vsprops"
+			Name="Debug-Dynamic|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)"
+			ConfigurationType="1"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm5-common-defaults.vsprops"
 			UseOfMFC="0"
 			ATLMinimizesCRunTimeLibraryUsage="false"
@@ -3064,5 +3064,5 @@
 				Name="VCLinkerTool"
 				AdditionalDependencies="iphlpapi.lib dsound.lib dxguid.lib netapi32.lib mswsock.lib ws2_32.lib odbc32.lib odbccp32.lib oleaut32.lib uuid.lib ole32.lib user32.lib"
-				OutputFile="..\bin\sample-debug-$(TargetCPU)-wm6std-vc$(VSVer)-$(ConfigurationName).exe"
+				OutputFile="..\bin\sample-debug-$(TargetCPU)-wm5ppc-vc$(VSVer)-$(ConfigurationName).exe"
 			/>
 			<Tool
@@ -3091,7 +3091,7 @@
 		</Configuration>
 		<Configuration
-			Name="Release-Static|Windows Mobile 6 Professional SDK (ARMV4I)"
-			ConfigurationType="1"
-			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-release-defaults.vsprops"
+			Name="Debug-Dynamic|Windows Mobile 5.0 Smartphone SDK (ARMV4I)"
+			ConfigurationType="1"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm5-common-defaults.vsprops"
 			UseOfMFC="0"
 			ATLMinimizesCRunTimeLibraryUsage="false"
@@ -3131,5 +3131,5 @@
 				Name="VCLinkerTool"
 				AdditionalDependencies="iphlpapi.lib dsound.lib dxguid.lib netapi32.lib mswsock.lib ws2_32.lib odbc32.lib odbccp32.lib oleaut32.lib uuid.lib ole32.lib user32.lib"
-				OutputFile="..\bin\sample-debug-$(TargetCPU)-wm6pro-vc$(VSVer)-$(ConfigurationName).exe"
+				OutputFile="..\bin\sample-debug-$(TargetCPU)-wm5sp-vc$(VSVer)-$(ConfigurationName).exe"
 			/>
 			<Tool
@@ -3300,5 +3300,5 @@
 			>
 			<File
-				RelativePath="..\src\samples\debug.c"
+				RelativePath="..\src\samples\debug.cpp"
 				>
 				<FileConfiguration
@@ -3339,4 +3339,22 @@
 				</FileConfiguration>
 				<FileConfiguration
+					Name="Debug|Windows Mobile 6 Standard SDK (ARMV4I)"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Debug|Windows Mobile 6 Professional SDK (ARMV4I)"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+				<FileConfiguration
 					Name="Release|Win32"
 					>
@@ -3375,4 +3393,22 @@
 				</FileConfiguration>
 				<FileConfiguration
+					Name="Release|Windows Mobile 6 Standard SDK (ARMV4I)"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Release|Windows Mobile 6 Professional SDK (ARMV4I)"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+				<FileConfiguration
 					Name="Debug-Static|Win32"
 					>
@@ -3411,4 +3447,22 @@
 				</FileConfiguration>
 				<FileConfiguration
+					Name="Debug-Static|Windows Mobile 6 Standard SDK (ARMV4I)"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Debug-Static|Windows Mobile 6 Professional SDK (ARMV4I)"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+				<FileConfiguration
 					Name="Release-Dynamic|Win32"
 					>
@@ -3447,4 +3501,22 @@
 				</FileConfiguration>
 				<FileConfiguration
+					Name="Release-Dynamic|Windows Mobile 6 Standard SDK (ARMV4I)"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Release-Dynamic|Windows Mobile 6 Professional SDK (ARMV4I)"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+				<FileConfiguration
 					Name="Debug-Dynamic|Win32"
 					>
@@ -3483,4 +3555,22 @@
 				</FileConfiguration>
 				<FileConfiguration
+					Name="Debug-Dynamic|Windows Mobile 6 Standard SDK (ARMV4I)"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Debug-Dynamic|Windows Mobile 6 Professional SDK (ARMV4I)"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+				<FileConfiguration
 					Name="Release-Static|Win32"
 					>
@@ -3519,14 +3609,14 @@
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug|Windows Mobile 6 Standard SDK (ARMV4I)"
-					>
-					<Tool
-						Name="VCCLCompilerTool"
-						AdditionalIncludeDirectories=""
-						PreprocessorDefinitions=""
-					/>
-				</FileConfiguration>
-				<FileConfiguration
-					Name="Debug|Windows Mobile 6 Professional SDK (ARMV4I)"
+					Name="Release-Static|Windows Mobile 6 Standard SDK (ARMV4I)"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						AdditionalIncludeDirectories=""
+						PreprocessorDefinitions=""
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Release-Static|Windows Mobile 6 Professional SDK (ARMV4I)"
 					>
 					<Tool
@@ -3555,22 +3645,4 @@
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release|Windows Mobile 6 Standard SDK (ARMV4I)"
-					>
-					<Tool
-						Name="VCCLCompilerTool"
-						AdditionalIncludeDirectories=""
-						PreprocessorDefinitions=""
-					/>
-				</FileConfiguration>
-				<FileConfiguration
-					Name="Release|Windows Mobile 6 Professional SDK (ARMV4I)"
-					>
-					<Tool
-						Name="VCCLCompilerTool"
-						AdditionalIncludeDirectories=""
-						PreprocessorDefinitions=""
-					/>
-				</FileConfiguration>
-				<FileConfiguration
 					Name="Release|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)"
 					>
@@ -3591,22 +3663,4 @@
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Static|Windows Mobile 6 Standard SDK (ARMV4I)"
-					>
-					<Tool
-						Name="VCCLCompilerTool"
-						AdditionalIncludeDirectories=""
-						PreprocessorDefinitions=""
-					/>
-				</FileConfiguration>
-				<FileConfiguration
-					Name="Debug-Static|Windows Mobile 6 Professional SDK (ARMV4I)"
-					>
-					<Tool
-						Name="VCCLCompilerTool"
-						AdditionalIncludeDirectories=""
-						PreprocessorDefinitions=""
-					/>
-				</FileConfiguration>
-				<FileConfiguration
 					Name="Debug-Static|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)"
 					>
@@ -3627,22 +3681,4 @@
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Release-Dynamic|Windows Mobile 6 Standard SDK (ARMV4I)"
-					>
-					<Tool
-						Name="VCCLCompilerTool"
-						AdditionalIncludeDirectories=""
-						PreprocessorDefinitions=""
-					/>
-				</FileConfiguration>
-				<FileConfiguration
-					Name="Release-Dynamic|Windows Mobile 6 Professional SDK (ARMV4I)"
-					>
-					<Tool
-						Name="VCCLCompilerTool"
-						AdditionalIncludeDirectories=""
-						PreprocessorDefinitions=""
-					/>
-				</FileConfiguration>
-				<FileConfiguration
 					Name="Release-Dynamic|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)"
 					>
@@ -3663,22 +3699,4 @@
 				</FileConfiguration>
 				<FileConfiguration
-					Name="Debug-Dynamic|Windows Mobile 6 Standard SDK (ARMV4I)"
-					>
-					<Tool
-						Name="VCCLCompilerTool"
-						AdditionalIncludeDirectories=""
-						PreprocessorDefinitions=""
-					/>
-				</FileConfiguration>
-				<FileConfiguration
-					Name="Debug-Dynamic|Windows Mobile 6 Professional SDK (ARMV4I)"
-					>
-					<Tool
-						Name="VCCLCompilerTool"
-						AdditionalIncludeDirectories=""
-						PreprocessorDefinitions=""
-					/>
-				</FileConfiguration>
-				<FileConfiguration
 					Name="Debug-Dynamic|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)"
 					>
@@ -3691,22 +3709,4 @@
 				<FileConfiguration
 					Name="Debug-Dynamic|Windows Mobile 5.0 Smartphone SDK (ARMV4I)"
-					>
-					<Tool
-						Name="VCCLCompilerTool"
-						AdditionalIncludeDirectories=""
-						PreprocessorDefinitions=""
-					/>
-				</FileConfiguration>
-				<FileConfiguration
-					Name="Release-Static|Windows Mobile 6 Standard SDK (ARMV4I)"
-					>
-					<Tool
-						Name="VCCLCompilerTool"
-						AdditionalIncludeDirectories=""
-						PreprocessorDefinitions=""
-					/>
-				</FileConfiguration>
-				<FileConfiguration
-					Name="Release-Static|Windows Mobile 6 Professional SDK (ARMV4I)"
 					>
 					<Tool
Index: /pjproject/trunk/pjsip-apps/src/pjsua/main.c
===================================================================
--- /pjproject/trunk/pjsip-apps/src/pjsua/main.c	(revision 4703)
+++ /pjproject/trunk/pjsip-apps/src/pjsua/main.c	(revision 4704)
@@ -122,3 +122,4 @@
 	}
     }
+    return 0;
 }
Index: /pjproject/trunk/pjsip-apps/src/pygui/account.py
===================================================================
--- /pjproject/trunk/pjsip-apps/src/pygui/account.py	(revision 4704)
+++ /pjproject/trunk/pjsip-apps/src/pygui/account.py	(revision 4704)
@@ -0,0 +1,239 @@
+# $Id$
+#
+# pjsua Python GUI Demo
+#
+# Copyright (C)2013 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 
+#
+import sys
+if sys.version_info[0] >= 3: # Python 3
+	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 random
+import pjsua2 as pj
+import _pjsua2
+import accountsetting
+import application
+import call
+import chat as ch
+
+# 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)
+
+		
+# 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)
+		
+		
+if __name__ == '__main__':
+	application.main()
Index: /pjproject/trunk/pjsip-apps/src/pygui/accountsetting.py
===================================================================
--- /pjproject/trunk/pjsip-apps/src/pygui/accountsetting.py	(revision 4704)
+++ /pjproject/trunk/pjsip-apps/src/pygui/accountsetting.py	(revision 4704)
@@ -0,0 +1,369 @@
+# $Id$
+#
+# pjsua Python GUI Demo
+#
+# Copyright (C)2013 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 
+#
+import sys
+if sys.version_info[0] >= 3: # Python 3
+	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 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()
+
+
+if __name__ == '__main__':
+	application.main()
Index: /pjproject/trunk/pjsip-apps/src/pygui/application.py
===================================================================
--- /pjproject/trunk/pjsip-apps/src/pygui/application.py	(revision 4704)
+++ /pjproject/trunk/pjsip-apps/src/pygui/application.py	(revision 4704)
@@ -0,0 +1,510 @@
+# $Id$
+#
+# pjsua Python GUI Demo
+#
+# Copyright (C)2013 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 
+#
+import sys
+if sys.version_info[0] >= 3: # Python 3
+	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 pjsua2 as pj
+import log
+import accountsetting
+import account
+import buddy
+import endpoint
+import settings
+
+import os
+import traceback
+
+	
+class Application(ttk.Frame):
+	"""
+	The Application main frame.
+	"""
+	def __init__(self):
+		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()
+		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'):
+		# Load config
+		if cfg_file and os.path.exists(cfg_file):
+			self.appConfig.loadFile(cfg_file)
+
+		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
+		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')
+
+def main():
+	#tk.CallWrapper = ExceptionCatcher
+	app = Application()
+	app.start()
+	app.mainloop()
+		
+if __name__ == '__main__':
+	main()
Index: /pjproject/trunk/pjsip-apps/src/pygui/buddy.py
===================================================================
--- /pjproject/trunk/pjsip-apps/src/pygui/buddy.py	(revision 4704)
+++ /pjproject/trunk/pjsip-apps/src/pygui/buddy.py	(revision 4704)
@@ -0,0 +1,152 @@
+# $Id$
+#
+# pjsua Python GUI Demo
+#
+# Copyright (C)2013 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 
+#
+import sys
+if sys.version_info[0] >= 3: # Python 3
+	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 random
+import pjsua2 as pj
+import endpoint
+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)
+
+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()
+
+
+if __name__ == '__main__':
+	application.main()
Index: /pjproject/trunk/pjsip-apps/src/pygui/call.py
===================================================================
--- /pjproject/trunk/pjsip-apps/src/pygui/call.py	(revision 4704)
+++ /pjproject/trunk/pjsip-apps/src/pygui/call.py	(revision 4704)
@@ -0,0 +1,104 @@
+# $Id$
+#
+# pjsua Python GUI Demo
+#
+# Copyright (C)2013 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 
+#
+import sys
+if sys.version_info[0] >= 3: # Python 3
+	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 random
+import pjsua2 as pj
+import application
+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
+
+	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
+			
+	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()
Index: /pjproject/trunk/pjsip-apps/src/pygui/chat.py
===================================================================
--- /pjproject/trunk/pjsip-apps/src/pygui/chat.py	(revision 4704)
+++ /pjproject/trunk/pjsip-apps/src/pygui/chat.py	(revision 4704)
@@ -0,0 +1,489 @@
+# $Id$
+#
+# pjsua Python GUI Demo
+#
+# Copyright (C)2013 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 
+#
+import sys
+if sys.version_info[0] >= 3: # Python 3
+	import tkinter as tk
+	from tkinter import ttk
+else:
+	import Tkinter as tk
+	import ttk
+
+import buddy
+import call
+import chatgui as gui
+import endpoint as ep
+import pjsua2 as pj
+import re
+
+SipUriRegex = re.compile('(sip|sips):([^:;>\@]*)@?([^:;>]*):?([^:;>]*)')
+ConfIdx = 1
+
+# 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)
+	
+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
+	
+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
+			sender = None
+			if self._callList[idx] and self._callList[idx].connected:
+				sender = self._callList[idx]
+			else:
+				sender = self._buddyList[idx]
+			assert(sender)
+				
+			try:
+				sender.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
+			sender = None
+			if self._callList[idx] and self._callList[idx].connected:
+				sender = self._callList[idx]
+			else:
+				sender = self._buddyList[idx]
+			assert(sender)
+			
+			try:
+				sender.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)
+			med_idx = self._getActiveMediaIdx(thecall)
+			si = thecall.getStreamInfo(med_idx)
+			stats_str = "Audio codec: %s/%s\n..." % (si.codecName, si.codecClockRate)
+			self._gui.audioSetStatsText(thecall.peerUri, stats_str)
+		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))
+
+			
+	# ** 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()
Index: /pjproject/trunk/pjsip-apps/src/pygui/chatgui.py
===================================================================
--- /pjproject/trunk/pjsip-apps/src/pygui/chatgui.py	(revision 4704)
+++ /pjproject/trunk/pjsip-apps/src/pygui/chatgui.py	(revision 4704)
@@ -0,0 +1,420 @@
+# $Id$
+#
+# pjsua Python GUI Demo
+#
+# Copyright (C)2013 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 
+#
+import sys
+if sys.version_info[0] >= 3: # Python 3
+	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
+
+
+class TextObserver:
+	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'] = ''
+
+class AudioState:
+	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
+			
+
+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=("Arial", "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
+		
+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
+				
+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()
Index: /pjproject/trunk/pjsip-apps/src/pygui/endpoint.py
===================================================================
--- /pjproject/trunk/pjsip-apps/src/pygui/endpoint.py	(revision 4704)
+++ /pjproject/trunk/pjsip-apps/src/pygui/endpoint.py	(revision 4704)
@@ -0,0 +1,53 @@
+# $Id$
+#
+# pjsua Python GUI Demo
+#
+# Copyright (C)2013 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 
+#
+import sys
+if sys.version_info[0] >= 3: # Python 3
+	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 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
+	
+	
+def validateUri(uri):
+	return Endpoint.instance.utilVerifyUri(uri) == pj.PJ_SUCCESS
+
+def validateSipUri(uri):
+	return Endpoint.instance.utilVerifySipUri(uri) == pj.PJ_SUCCESS
+
+
+if __name__ == '__main__':
+	application.main()
Index: /pjproject/trunk/pjsip-apps/src/pygui/log.py
===================================================================
--- /pjproject/trunk/pjsip-apps/src/pygui/log.py	(revision 4704)
+++ /pjproject/trunk/pjsip-apps/src/pygui/log.py	(revision 4704)
@@ -0,0 +1,127 @@
+# $Id$
+#
+# pjsua Python GUI Demo
+#
+# Copyright (C)2013 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 
+#
+import sys
+if sys.version_info[0] >= 3: # Python 3
+	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 pjsua2 as pj
+import application
+
+
+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)
+		
+	
+def writeLog2(level, msg):
+	if LogWindow.instance:
+		LogWindow.instance.addLog2(level, msg)	
+
+def writeLog(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)
+
+if __name__ == '__main__':
+	application.main()
Index: /pjproject/trunk/pjsip-apps/src/pygui/settings.py
===================================================================
--- /pjproject/trunk/pjsip-apps/src/pygui/settings.py	(revision 4704)
+++ /pjproject/trunk/pjsip-apps/src/pygui/settings.py	(revision 4704)
@@ -0,0 +1,362 @@
+# $Id$
+#
+# pjsua Python GUI Demo
+#
+# Copyright (C)2013 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 
+#
+import sys
+if sys.version_info[0] >= 3: # Python 3
+	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 pjsua2 as pj
+#import application
+
+# Transport setting
+class SipTransportConfig:
+	def __init__(self, type, enabled):
+		#pj.PersistentObject.__init__(self)
+		self.type = type
+		self.enabled = enabled
+		self.config = pj.TransportConfig()
+	def readObject(self, node):
+		child_node = node.readContainer("SipTransport")
+		self.type = child_node.readInt("type")
+		self.enabled = child_node.readBool("enabled")
+		self.config.readObject(child_node)
+	def writeObject(self, node):
+		child_node = node.writeNewContainer("SipTransport")
+		child_node.writeInt("type", self.type)
+		child_node.writeBool("enabled", self.enabled)
+		self.config.writeObject(child_node)
+
+# Account setting with buddy list
+class AccConfig:
+	def __init__(self):
+		self.enabled = True
+		self.config = pj.AccountConfig()
+		self.buddyConfigs = []
+	def readObject(self, node):
+		acc_node = node.readContainer("Account")
+		self.enabled = acc_node.readBool("enabled")
+		self.config.readObject(acc_node)
+		buddy_node = acc_node.readArray("buddies")
+		while buddy_node.hasUnread():
+			buddy_cfg = pj.BuddyConfig()
+			buddy_cfg.readObject(buddy_node)
+			self.buddyConfigs.append(buddy_cfg)
+	def writeObject(self, node):
+		acc_node = node.writeNewContainer("Account")
+		acc_node.writeBool("enabled", self.enabled)
+		self.config.writeObject(acc_node)
+		buddy_node = acc_node.writeNewArray("buddies")
+		for buddy in self.buddyConfigs:
+			buddy_node.writeObject(buddy)
+
+	
+# Master settings
+class AppConfig:
+	def __init__(self):
+		self.epConfig = pj.EpConfig()	# pj.EpConfig()
+		self.udp = SipTransportConfig(pj.PJSIP_TRANSPORT_UDP, True)
+		self.tcp = SipTransportConfig(pj.PJSIP_TRANSPORT_TCP, True)
+		self.tls = SipTransportConfig(pj.PJSIP_TRANSPORT_TLS, False)
+		self.accounts = []		# Array of AccConfig
+		
+	def loadFile(self, file):
+		json = pj.JsonDocument()
+		json.loadFile(file)
+		root = json.getRootContainer()
+		self.epConfig = pj.EpConfig()
+		self.epConfig.readObject(root)
+		
+		tp_node = root.readArray("transports")
+		self.udp.readObject(tp_node)
+		self.tcp.readObject(tp_node)
+		if tp_node.hasUnread():
+			self.tls.readObject(tp_node)
+			
+		acc_node = root.readArray("accounts")
+		while acc_node.hasUnread():
+			acfg = AccConfig()
+			acfg.readObject(acc_node)
+			self.accounts.append(acfg)
+			
+	def saveFile(self,file):
+		json = pj.JsonDocument()
+		
+		# Write endpoint config
+		json.writeObject(self.epConfig)
+		
+		# Write transport config
+		tp_node = json.writeNewArray("transports")
+		self.udp.writeObject(tp_node)
+		self.tcp.writeObject(tp_node)
+		self.tls.writeObject(tp_node)
+		
+		# Write account configs
+		node = json.writeNewArray("accounts")
+		for acc in self.accounts:
+			acc.writeObject(node)
+				
+		json.saveFile(file)
+		
+
+# Settings dialog
+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.title('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.createNetworkTab()
+		self.createMediaTab()
+
+	def createBasicTab(self):
+		# Prepare the variables to set/receive values from GUI
+		self.cfgLogFile = tk.StringVar(value=self.cfg.epConfig.logConfig.filename)
+		self.cfgLogAppend = tk.BooleanVar(value=True if (self.cfg.epConfig.logConfig.fileFlags & pj.PJ_O_APPEND) else False)
+		
+		# 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='User Agent:').grid(row=row, column=0, sticky=tk.E, pady=2, padx=8)
+		ttk.Label(frm, text=self.cfg.epConfig.uaConfig.userAgent).grid(row=row, column=1, sticky=tk.W, pady=2, padx=6)
+		row += 1
+		ttk.Label(frm, text='Max calls:').grid(row=row, column=0, sticky=tk.E, pady=2, padx=8)
+		ttk.Label(frm, text=str(self.cfg.epConfig.uaConfig.maxCalls)).grid(row=row, column=1, sticky=tk.W, pady=2, padx=6)
+		row += 1
+		ttk.Label(frm, text='Log file:').grid(row=row, column=0, sticky=tk.E, pady=2, padx=8)
+		ttk.Entry(frm, textvariable=self.cfgLogFile, width=32).grid(row=row, column=1, sticky=tk.W, padx=6)
+		row += 1
+		ttk.Checkbutton(frm, text='Append log file', variable=self.cfgLogAppend).grid(row=row, column=1, sticky=tk.W, padx=6, pady=2)
+
+		self.wTab.add(frm, text='Basic')
+		
+	def createNetworkTab(self):
+		self.cfgNameserver = tk.StringVar()
+		if len(self.cfg.epConfig.uaConfig.nameserver):
+			self.cfgNameserver.set(self.cfg.epConfig.uaConfig.nameserver[0])
+		self.cfgStunServer = tk.StringVar()
+		if len(self.cfg.epConfig.uaConfig.stunServer):
+			self.cfgStunServer.set(self.cfg.epConfig.uaConfig.stunServer[0])
+		self.cfgStunIgnoreError = tk.BooleanVar(value=self.cfg.epConfig.uaConfig.stunIgnoreFailure)
+		
+		self.cfgUdpEnabled = tk.BooleanVar(value=self.cfg.udp.enabled)
+		self.cfgUdpPort = tk.IntVar(value=self.cfg.udp.config.port)
+		self.cfgTcpEnabled = tk.BooleanVar(value=self.cfg.tcp.enabled)
+		self.cfgTcpPort = tk.IntVar(value=self.cfg.tcp.config.port)
+		self.cfgTlsEnabled = tk.BooleanVar(value=self.cfg.tls.enabled)
+		self.cfgTlsPort = tk.IntVar(value=self.cfg.tls.config.port)
+		
+		self.cfgTlsCaFile = tk.StringVar(value=self.cfg.tls.config.tlsConfig.CaListFile)
+		self.cfgTlsCertFile = tk.StringVar(value=self.cfg.tls.config.tlsConfig.certFile)
+		self.cfgTlsVerifyClient = tk.BooleanVar(value=self.cfg.tls.config.tlsConfig.verifyClient)
+		self.cfgTlsVerifyServer = tk.BooleanVar(value=self.cfg.tls.config.tlsConfig.verifyServer)
+		
+		# 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='UDP transport:').grid(row=row, column=0, sticky=tk.E, pady=2, padx=8)
+		ttk.Checkbutton(frm, text='Enable UDP transport', variable=self.cfgUdpEnabled).grid(row=row, column=0, sticky=tk.W, padx=6, pady=2)
+		row += 1
+		ttk.Label(frm, text='UDP port:').grid(row=row, column=0, sticky=tk.E, pady=2, padx=8)
+		tk.Spinbox(frm, from_=0, to=65535, textvariable=self.cfgUdpPort, width=5).grid(row=row, column=1, sticky=tk.W, padx=6)
+		ttk.Label(frm, text='(0 for any)').grid(row=row, column=1, sticky=tk.E, pady=6, padx=6)
+		row += 1
+		#ttk.Label(frm, text='TCP transport:').grid(row=row, column=0, sticky=tk.E, pady=2, padx=8)
+		ttk.Checkbutton(frm, text='Enable TCP transport', variable=self.cfgTcpEnabled).grid(row=row, column=0, sticky=tk.W, padx=6, pady=2)
+		row += 1
+		ttk.Label(frm, text='TCP port:').grid(row=row, column=0, sticky=tk.E, pady=2, padx=8)
+		tk.Spinbox(frm, from_=0, to=65535, textvariable=self.cfgTcpPort, width=5).grid(row=row, column=1, sticky=tk.W, padx=6)
+		ttk.Label(frm, text='(0 for any)').grid(row=row, column=1, sticky=tk.E, pady=6, padx=6)
+		row += 1
+		#ttk.Label(frm, text='TLS transport:').grid(row=row, column=0, sticky=tk.E, pady=2, padx=8)
+		ttk.Checkbutton(frm, text='Enable TLS transport', variable=self.cfgTlsEnabled).grid(row=row, column=0, sticky=tk.W, padx=6, pady=2)
+		row += 1
+		ttk.Label(frm, text='TLS port:').grid(row=row, column=0, sticky=tk.E, pady=2, padx=8)
+		tk.Spinbox(frm, from_=0, to=65535, textvariable=self.cfgTlsPort, width=5).grid(row=row, column=1, sticky=tk.W, padx=6)
+		ttk.Label(frm, text='(0 for any)').grid(row=row, column=1, sticky=tk.E, pady=6, padx=6)
+		row += 1
+		ttk.Label(frm, text='TLS CA file:').grid(row=row, column=0, sticky=tk.E, pady=2, padx=8)
+		ttk.Entry(frm, textvariable=self.cfgTlsCaFile, width=32).grid(row=row, column=1, sticky=tk.W, padx=6)
+		row += 1
+		ttk.Label(frm, text='TLS cert file:').grid(row=row, column=0, sticky=tk.E, pady=2, padx=8)
+		ttk.Entry(frm, textvariable=self.cfgTlsCertFile, width=32).grid(row=row, column=1, sticky=tk.W, padx=6)
+		row += 1
+		ttk.Checkbutton(frm, text='TLS verify server', variable=self.cfgTlsVerifyServer).grid(row=row, column=1, sticky=tk.W, padx=6, pady=2)
+		row += 1
+		ttk.Checkbutton(frm, text='TLS verify client', variable=self.cfgTlsVerifyClient).grid(row=row, column=1, sticky=tk.W, padx=6, pady=2)
+		row += 1
+		ttk.Label(frm, text='DNS and STUN:').grid(row=row, column=0, sticky=tk.W, pady=2, padx=8)
+		row += 1
+		ttk.Label(frm, text='Nameserver:').grid(row=row, column=0, sticky=tk.E, pady=2, padx=8)
+		ttk.Entry(frm, textvariable=self.cfgNameserver, width=32).grid(row=row, column=1, sticky=tk.W, padx=6)
+		row += 1
+		ttk.Label(frm, text='STUN Server:').grid(row=row, column=0, sticky=tk.E, pady=2, padx=8)
+		ttk.Entry(frm, textvariable=self.cfgStunServer, width=32).grid(row=row, column=1, sticky=tk.W, padx=6)
+		row += 1
+		ttk.Checkbutton(frm, text='Ignore STUN failure at startup', variable=self.cfgStunIgnoreError).grid(row=row, column=1, sticky=tk.W, padx=6, pady=2)
+		
+		self.wTab.add(frm, text='Network')
+
+	def createMediaTab(self):
+		self.cfgClockrate = tk.IntVar(value=self.cfg.epConfig.medConfig.clockRate)
+		self.cfgSndClockrate = tk.IntVar(value=self.cfg.epConfig.medConfig.sndClockRate)
+		self.cfgAudioPtime = tk.IntVar(value=self.cfg.epConfig.medConfig.audioFramePtime)
+		self.cfgMediaQuality = tk.IntVar(value=self.cfg.epConfig.medConfig.quality)
+		self.cfgCodecPtime = tk.IntVar(value=self.cfg.epConfig.medConfig.ptime)
+		self.cfgVad = tk.BooleanVar(value=not self.cfg.epConfig.medConfig.noVad)
+		self.cfgEcTailLen = tk.IntVar(value=self.cfg.epConfig.medConfig.ecTailLen)
+		
+		# 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='Max media ports:').grid(row=row, column=0, sticky=tk.E, pady=2, padx=8)
+		ttk.Label(frm, text=str(self.cfg.epConfig.medConfig.maxMediaPorts)).grid(row=row, column=1, sticky=tk.W, pady=2, padx=6)
+		row += 1
+		ttk.Label(frm, text='Core clock rate:').grid(row=row, column=0, sticky=tk.E, pady=2, padx=8)
+		tk.Spinbox(frm, from_=8000, to=48000, increment=8000, textvariable=self.cfgClockrate, width=5).grid(row=row, column=1, sticky=tk.W, padx=6)
+		row += 1
+		ttk.Label(frm, text='Snd device clock rate:').grid(row=row, column=0, sticky=tk.E, pady=2, padx=8)
+		tk.Spinbox(frm, from_=0, to=48000, increment=8000, textvariable=self.cfgSndClockrate, width=5).grid(row=row, column=1, sticky=tk.W, padx=6)
+		ttk.Label(frm, text='(0: follow core)').grid(row=row, column=1, sticky=tk.E, pady=6, padx=6)
+		row += 1
+		ttk.Label(frm, text='Core ptime:').grid(row=row, column=0, sticky=tk.E, pady=2, padx=8)
+		tk.Spinbox(frm, from_=10, to=400, increment=10, textvariable=self.cfgAudioPtime, width=3).grid(row=row, column=1, sticky=tk.W, padx=6)
+		row += 1
+		ttk.Label(frm, text='RTP ptime:').grid(row=row, column=0, sticky=tk.E, pady=2, padx=8)
+		tk.Spinbox(frm, from_=20, to=400, increment=10, textvariable=self.cfgCodecPtime, width=3).grid(row=row, column=1, sticky=tk.W, padx=6)
+		row += 1
+		ttk.Label(frm, text='Media quality (1-10):').grid(row=row, column=0, sticky=tk.E, pady=2, padx=8)
+		tk.Spinbox(frm, from_=1, to=10, textvariable=self.cfgMediaQuality, width=5).grid(row=row, column=1, sticky=tk.W, padx=6)
+		row += 1
+		ttk.Label(frm, text='VAD:').grid(row=row, column=0, sticky=tk.E, pady=2, padx=8)
+		ttk.Checkbutton(frm, text='Enable', variable=self.cfgVad).grid(row=row, column=1, sticky=tk.W, padx=6, pady=2)
+		row += 1
+		ttk.Label(frm, text='Echo canceller tail length:').grid(row=row, column=0, sticky=tk.E, pady=2, padx=8)
+		tk.Spinbox(frm, from_=0, to=400, increment=10, textvariable=self.cfgEcTailLen, width=3).grid(row=row, column=1, sticky=tk.W, padx=6)
+		ttk.Label(frm, text='(ms, 0 to disable)').grid(row=row, column=1, sticky=tk.E, pady=6, padx=6)
+		
+		self.wTab.add(frm, text='Media')
+
+	def onOk(self):
+		# Check basic settings
+		errors = "";
+		if errors:
+			msgbox.showerror("Error detected:", errors)
+			return
+		
+		# Basic settings
+		self.cfg.epConfig.logConfig.filename = self.cfgLogFile.get()
+		flags = pj.PJ_O_APPEND if self.cfgLogAppend.get() else 0
+		self.cfg.epConfig.logConfig.fileFlags = self.cfg.epConfig.logConfig.fileFlags | flags 
+		
+		# Network settings
+		self.cfg.epConfig.uaConfig.nameserver.clear()
+		if len(self.cfgNameserver.get()): 
+			self.cfg.epConfig.uaConfig.nameserver.append(self.cfgNameserver.get())
+		self.cfg.epConfig.uaConfig.stunServer.clear()
+		if len(self.cfgStunServer.get()):
+			self.cfg.epConfig.uaConfig.stunServer.append(self.cfgStunServer.get())
+			
+		self.cfg.epConfig.uaConfig.stunIgnoreFailure = self.cfgStunIgnoreError.get()
+		
+		self.cfg.udp.enabled 	= self.cfgUdpEnabled.get()
+		self.cfg.udp.config.port = self.cfgUdpPort.get()
+		self.cfg.tcp.enabled 	= self.cfgTcpEnabled.get()
+		self.cfg.tcp.config.port = self.cfgTcpPort.get()
+		self.cfg.tls.enabled 	= self.cfgTlsEnabled.get()
+		self.cfg.tls.config.port = self.cfgTlsPort.get()
+		
+		self.cfg.tls.config.tlsConfig.CaListFile = self.cfgTlsCaFile.get()
+		self.cfg.tls.config.tlsConfig.certFile   = self.cfgTlsCertFile.get()
+		self.cfg.tls.config.tlsConfig.verifyClient = self.cfgTlsVerifyClient.get()
+		self.cfg.tls.config.tlsConfig.verifyServer = self.cfgTlsVerifyServer.get()
+
+		# Media
+		self.cfg.epConfig.medConfig.clockRate	= self.cfgClockrate.get()
+		self.cfg.epConfig.medConfig.sndClockRate = self.cfgSndClockrate.get()
+		self.cfg.epConfig.medConfig.audioFramePtime = self.cfgAudioPtime.get()
+		self.cfg.epConfig.medConfig.quality	= self.cfgMediaQuality.get()
+		self.cfg.epConfig.medConfig.ptime	= self.cfgCodecPtime.get()
+		self.cfg.epConfig.medConfig.noVad	= not self.cfgVad.get()
+		self.cfg.epConfig.medConfig.ecTailLen	= self.cfgEcTailLen.get()
+		
+		self.isOk = True
+		self.destroy()
+		
+	def onCancel(self):
+		self.destroy()
+
+
+if __name__ == '__main__':
+	#application.main()
+	acfg = AppConfig()
+	acfg.loadFile('pygui.js')
+
+	dlg = Dialog(None, acfg)
+	if dlg.doModal():
+		acfg.saveFile('pygui.js')
+		
Index: project/trunk/pjsip-apps/src/samples/debug.c
===================================================================
--- /pjproject/trunk/pjsip-apps/src/samples/debug.c	(revision 4703)
+++ 	(revision )
@@ -1,32 +1,0 @@
-/* $Id$ */
-/* 
- * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
- * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
- */
-
-/*
- * PURPOSE:
- *   The purpose of this file is to allow debugging of a sample application
- *   using MSVC IDE.
- */
-
-/* To debug a sample application, include the source file here.
- * E.g.:
- *  #include "playfile.c"
- */
-#include "icedemo.c"
-
Index: /pjproject/trunk/pjsip-apps/src/samples/debug.cpp
===================================================================
--- /pjproject/trunk/pjsip-apps/src/samples/debug.cpp	(revision 4704)
+++ /pjproject/trunk/pjsip-apps/src/samples/debug.cpp	(revision 4704)
@@ -0,0 +1,32 @@
+/* $Id$ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+
+/*
+ * PURPOSE:
+ *   The purpose of this file is to allow debugging of a sample application
+ *   using MSVC IDE.
+ */
+
+/* To debug a sample application, include the source file here.
+ * E.g.:
+ *  #include "playfile.c"
+ */
+#include "icedemo.c"
+
Index: /pjproject/trunk/pjsip-apps/src/samples/pjsua2_demo.cpp
===================================================================
--- /pjproject/trunk/pjsip-apps/src/samples/pjsua2_demo.cpp	(revision 4704)
+++ /pjproject/trunk/pjsip-apps/src/samples/pjsua2_demo.cpp	(revision 4704)
@@ -0,0 +1,290 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2008-2013 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 <pjsua2.hpp>
+#include <iostream>
+#include <memory>
+#include <pj/file_access.h>
+
+using namespace pj;
+
+class MyAccount;
+
+class MyCall : public Call
+{
+private:
+    MyAccount *myAcc;
+
+public:
+    MyCall(Account &acc, int call_id = PJSUA_INVALID_ID)
+    : Call(acc, call_id)
+    {
+        myAcc = (MyAccount *)&acc;
+    }
+    
+    virtual void onCallState(OnCallStateParam &prm);
+};
+
+class MyAccount : public Account
+{
+public:
+    std::vector<Call *> calls;
+    
+public:
+    MyAccount()
+    {}
+
+    ~MyAccount()
+    {
+        std::cout << "*** Account is being deleted: No of calls="
+                  << calls.size() << std::endl;
+    }
+    
+    void removeCall(Call *call)
+    {
+        for (std::vector<Call *>::iterator it = calls.begin();
+             it != calls.end(); ++it)
+        {
+            if (*it == call) {
+                calls.erase(it);
+                break;
+            }
+        }
+    }
+
+    virtual void onRegState(OnRegStateParam &prm)
+    {
+	AccountInfo ai = getInfo();
+	std::cout << (ai.regIsActive? "*** Register: code=" : "*** Unregister: code=")
+		  << prm.code << std::endl;
+    }
+    
+    virtual void onIncomingCall(OnIncomingCallParam &iprm)
+    {
+        Call *call = new MyCall(*this, iprm.callId);
+        CallInfo ci = call->getInfo();
+        CallOpParam prm;
+        
+        std::cout << "*** Incoming Call: " <<  ci.remoteUri << " ["
+                  << ci.stateText << "]" << std::endl;
+        
+        calls.push_back(call);
+        prm.statusCode = (pjsip_status_code)200;
+        call->answer(prm);
+    }
+};
+
+void MyCall::onCallState(OnCallStateParam &prm)
+{
+    CallInfo ci = getInfo();
+    std::cout << "*** Call: " <<  ci.remoteUri << " [" << ci.stateText
+              << "]" << std::endl;
+    
+    if (ci.state == PJSIP_INV_STATE_DISCONNECTED) {
+        myAcc->removeCall(this);
+        /* Delete the call */
+        delete this;
+    }
+}
+
+static void mainProg1() throw(Error)
+{
+    Endpoint ep;
+
+    // Create library
+    ep.libCreate();
+
+    // Init library
+    EpConfig ep_cfg;
+    ep_cfg.logConfig.level = 4;
+    ep.libInit( ep_cfg );
+
+    // Transport
+    TransportConfig tcfg;
+    tcfg.port = 5060;
+    ep.transportCreate(PJSIP_TRANSPORT_UDP, tcfg);
+
+    // Start library
+    ep.libStart();
+    std::cout << "*** PJSUA2 STARTED ***" << std::endl;
+
+    // Add account
+    AccountConfig acc_cfg;
+    acc_cfg.idUri = "sip:test1@pjsip.org";
+    acc_cfg.regConfig.registrarUri = "sip:pjsip.org";
+    acc_cfg.sipConfig.authCreds.push_back( AuthCredInfo("digest", "*",
+                                                        "test1", 0, "test1") );
+    std::auto_ptr<MyAccount> acc(new MyAccount);
+    acc->create(acc_cfg);
+    
+    pj_thread_sleep(2000);
+    
+    // Make outgoing call
+    Call *call = new MyCall(*acc);
+    acc->calls.push_back(call);
+    CallOpParam prm(true);
+    prm.opt.audioCount = 1;
+    prm.opt.videoCount = 0;
+    call->makeCall("sip:test1@pjsip.org", prm);
+    
+    // Hangup all calls
+    pj_thread_sleep(8000);
+    ep.hangupAllCalls();
+    pj_thread_sleep(4000);
+    
+    // Destroy library
+    std::cout << "*** PJSUA2 SHUTTING DOWN ***" << std::endl;
+}
+
+void mainProg2() throw(Error)
+{
+    Endpoint ep;
+
+    // Create library
+    ep.libCreate();
+
+    string json_str;
+
+    {
+	EpConfig epCfg;
+	JsonDocument jDoc;
+
+	epCfg.uaConfig.maxCalls = 61;
+	epCfg.uaConfig.userAgent = "Just JSON Test";
+	epCfg.uaConfig.stunServer.push_back("stun1.pjsip.org");
+	epCfg.uaConfig.stunServer.push_back("stun2.pjsip.org");
+	epCfg.logConfig.filename = "THE.LOG";
+
+	jDoc.writeObject(epCfg);
+	json_str = jDoc.saveString();
+	std::cout << json_str << std::endl << std::endl;
+    }
+
+    {
+	EpConfig epCfg;
+	JsonDocument rDoc;
+	string output;
+
+	rDoc.loadString(json_str);
+	rDoc.readObject(epCfg);
+
+	JsonDocument wDoc;
+
+	wDoc.writeObject(epCfg);
+	json_str = wDoc.saveString();
+	std::cout << json_str << std::endl << std::endl;
+
+	wDoc.saveFile("jsontest.js");
+    }
+
+    {
+	EpConfig epCfg;
+	JsonDocument rDoc;
+
+	rDoc.loadFile("jsontest.js");
+	rDoc.readObject(epCfg);
+	pj_file_delete("jsontest.js");
+    }
+
+    ep.libDestroy();
+}
+
+void mainProg() throw(Error)
+{
+    Endpoint ep;
+
+    // Create library
+    ep.libCreate();
+
+    string json_str;
+
+    {
+	JsonDocument jdoc;
+	AccountConfig accCfg;
+
+	accCfg.idUri = "\"Just Test\" <sip:test@pjsip.org>";
+	accCfg.regConfig.registrarUri = "sip:pjsip.org";
+	SipHeader h;
+	h.hName = "X-Header";
+	h.hValue = "User header";
+	accCfg.regConfig.headers.push_back(h);
+
+	accCfg.sipConfig.proxies.push_back("<sip:sip.pjsip.org;transport=tcp>");
+	accCfg.sipConfig.proxies.push_back("<sip:sip.pjsip.org;transport=tls>");
+
+	accCfg.mediaConfig.transportConfig.tlsConfig.ciphers.push_back(1);
+	accCfg.mediaConfig.transportConfig.tlsConfig.ciphers.push_back(2);
+	accCfg.mediaConfig.transportConfig.tlsConfig.ciphers.push_back(3);
+
+	AuthCredInfo aci;
+	aci.scheme = "digest";
+	aci.username = "test";
+	aci.data = "passwd";
+	aci.realm = "*";
+	accCfg.sipConfig.authCreds.push_back(aci);
+
+	jdoc.writeObject(accCfg);
+	json_str = jdoc.saveString();
+	std::cout << "Original:" << std::endl;
+	std::cout << json_str << std::endl << std::endl;
+    }
+
+    {
+	JsonDocument rdoc;
+
+	rdoc.loadString(json_str);
+	AccountConfig accCfg;
+	rdoc.readObject(accCfg);
+
+	JsonDocument wdoc;
+	wdoc.writeObject(accCfg);
+	json_str = wdoc.saveString();
+
+	std::cout << "Parsed:" << std::endl;
+	std::cout << json_str << std::endl << std::endl;
+    }
+
+    ep.libDestroy();
+}
+
+int main()
+{
+    int ret = 0;
+
+    /* Test endpoint instantiation and destruction without libCreate(),
+     * libInit() etc.
+     */
+    {
+	Endpoint ep;
+	ep.natDetectType();
+	{
+	}
+    }
+
+    try {
+	mainProg1();
+	std::cout << "Success" << std::endl;
+    } catch (Error & err) {
+	std::cout << "Exception: " << err.info() << std::endl;
+	ret = 1;
+    }
+
+    return ret;
+}
+
+
Index: /pjproject/trunk/pjsip-apps/src/swig/Makefile
===================================================================
--- /pjproject/trunk/pjsip-apps/src/swig/Makefile	(revision 4704)
+++ /pjproject/trunk/pjsip-apps/src/swig/Makefile	(revision 4704)
@@ -0,0 +1,32 @@
+include ../../../build.mak
+
+ifneq ($(findstring android,$(TARGET_NAME)),)
+  # no python for android
+  DIRS = java
+else
+  DIRS = python java
+endif
+
+export SWIG_FLAGS=-I../../../../pjlib/include \
+		   -I../../../../pjlib-util/include \
+		   -I../../../../pjmedia/include \
+		   -I../../../../pjsip/include \
+		   -I../../../../pjnath/include -c++ 
+export SRC_DIR=../../../../pjsip/include
+export SRCS=$(SRC_DIR)/pjsua2/endpoint.hpp $(SRC_DIR)/pjsua2/types.hpp
+
+.PHONY: all clean dep depend distclean print realclean install uninstall
+
+all: symbols.i
+
+all clean dep depend distclean print realclean install uninstall:
+	for dir in $(DIRS); do \
+		if $(MAKE) $(MAKE_FLAGS) -C $$dir $@; then \
+		    true; \
+		else \
+		    exit 1; \
+		fi; \
+	done
+
+symbols.i: symbols.lst
+	python importsym.py
Index: /pjproject/trunk/pjsip-apps/src/swig/importsym.py
===================================================================
--- /pjproject/trunk/pjsip-apps/src/swig/importsym.py	(revision 4704)
+++ /pjproject/trunk/pjsip-apps/src/swig/importsym.py	(revision 4704)
@@ -0,0 +1,193 @@
+# $Id$
+#
+# importsym.py: Import C symbol decls (structs, enums, etc) and write them
+#               to another file 
+#
+# Copyright (C)2013 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 
+#
+import pycparser
+from pycparser import c_generator
+import sys
+import os
+
+def which(program):
+	import os
+	def is_exe(fpath):
+		return os.path.isfile(fpath) and os.access(fpath, os.X_OK)
+	
+	if sys.platform == 'win32' and not program.endswith(".exe"):
+		program += ".exe"
+
+	fpath, fname = os.path.split(program)
+	if fpath:
+		if is_exe(program):
+			return program
+	else:
+		for path in os.environ["PATH"].split(os.pathsep):
+			path = path.strip('"')
+			exe_file = os.path.join(path, program)
+			if is_exe(exe_file):
+				return exe_file
+	return None
+
+#
+PJ_ROOT_PATH = "../../../"
+   
+# CPP is needed by pycparser.
+CPP_PATH = which("cpp")
+if not CPP_PATH:
+	print 'Error: need to have cpp in PATH'
+	sys.exit(1)
+
+# Hardcoded!
+if sys.platform == 'win32':
+	PYCPARSER_DIR="C:/devs/tools/pycparser"
+elif sys.platform == "linux2":
+	PYCPARSER_DIR="/home/bennylp/Desktop/opt/src/pycparser-master"
+else:
+	PYCPARSER_DIR="/Library/Python/2.7/site-packages/pycparser"
+
+if not os.path.exists(PYCPARSER_DIR + '/utils/fake_libc_include'):
+	print "Error: couldn't find pycparser utils in '%s'" % PYPARSER_DIR 
+	sys.exit(1)
+
+# Heading, to be placed before the source files
+C_HEADING_SECTION = """
+#define PJ_AUTOCONF		1
+#define jmp_buf			int
+#define __attribute__(x)
+"""
+
+# CPP (C preprocessor) settings
+CPP_CFLAGS   = [
+	'-I' + PYCPARSER_DIR + '/utils/fake_libc_include',
+	"-I" + PJ_ROOT_PATH + "pjlib/include",
+	"-I" + PJ_ROOT_PATH + "pjlib-util/include",
+	"-I" + PJ_ROOT_PATH + "pjnath/include",
+	"-I" + PJ_ROOT_PATH + "pjmedia/include",
+	"-I" + PJ_ROOT_PATH + "pjsip/include"
+	]
+
+
+class SymbolVisitor(pycparser.c_ast.NodeVisitor):
+	def __init__(self, names):
+		self.nodeDict = {}
+		for name in names:
+			self.nodeDict[name] = None
+		
+	def _add(self, node):
+		if self.nodeDict.has_key(node.name):
+			self.nodeDict[node.name] = node
+		
+	def visit_Struct(self, node):
+		self._add(node)
+		
+	def visit_Enum(self, node):
+		self._add(node)
+
+	def visit_Typename(self, node):
+		self._add(node)
+		
+	def visit_Typedef(self, node):
+		self._add(node)
+
+		
+TEMP_FILE="tmpsrc.h"
+		
+class SymbolImporter:
+	"""
+	Import C selected declarations from C source file and move it
+	to another file.
+	
+	Parameters:
+	 - listfile	Path of file containing list of C source file
+	                and identifier names to be imported. The format
+	                of the listfile is:
+	                
+	                filename        name1  name2  name3
+	                
+	                for example:
+	                
+	                pj/sock_qos.h	pj_qos_type  pj_qos_flag
+	                pj/types.h	pj_status_t  PJ_SUCCESS
+	"""
+	def __init__(self):
+		pass
+	
+	def process(self, listfile, outfile):
+		
+		# Read listfile
+		f = open(listfile)
+		lines = f.readlines()
+		f.close()
+		
+		# Process each line in list file, while generating the
+		# temporary C file to be processed by pycparser
+		f = open(TEMP_FILE, "w")
+		f.write(C_HEADING_SECTION)
+		names = []
+		fcnt = 0
+		for line in lines:
+			spec = line.split()
+			if len(spec) < 2:
+				continue
+			fcnt += 1
+			f.write("#include <%s>\n" % spec[0])
+			names.extend(spec[1:])
+		f.close()
+		print 'Parsing %d symbols from %d files..' % (len(names), fcnt)
+		
+		# Parse the temporary C file
+		ast = pycparser.parse_file(TEMP_FILE, use_cpp=True, cpp_path=CPP_PATH, cpp_args=CPP_CFLAGS)
+		os.remove(TEMP_FILE)
+		
+		# Filter the declarations that we wanted
+		print 'Filtering..'
+		visitor = SymbolVisitor(names)
+		visitor.visit(ast)
+	
+		# Print symbol declarations to outfile
+		print 'Writing declarations..'
+		f = open(outfile, 'w')
+		f.write("// This file is autogenerated by importsym script, do not modify!\n\n")
+		gen = pycparser.c_generator.CGenerator()
+		for name in names:
+			node = visitor.nodeDict[name]
+			if not node:
+				print "  ** Warning: declaration for '%s' is not found **" % k
+			else:
+				print "  writing '%s'.." % name
+				output = gen.visit(node) + ";\n\n"
+				f.write(output)
+		f.close()
+		print "Done."
+
+
+if __name__ == "__main__":
+	print "Importing symbols: 'symbols.lst' --> 'symbols.i'"
+	si = SymbolImporter()
+	si.process("symbols.lst", "symbols.i")
+	try:
+		os.remove("lextab.py")
+	except OSError:
+		pass
+	try:
+		os.remove("yacctab.py")
+	except OSError:
+		pass
+	
+	
Index: /pjproject/trunk/pjsip-apps/src/swig/java/Makefile
===================================================================
--- /pjproject/trunk/pjsip-apps/src/swig/java/Makefile	(revision 4704)
+++ /pjproject/trunk/pjsip-apps/src/swig/java/Makefile	(revision 4704)
@@ -0,0 +1,124 @@
+include ../../../../build.mak
+
+ifneq ($(findstring android,$(TARGET_NAME)),)
+ OS=android
+else
+ ifneq ($(findstring darwin,$(TARGET_NAME)),)
+  OS=darwin
+ endif
+endif
+
+OUT_DIR=output
+ifeq ($(OS),Windows_NT)
+  LIBPJSUA2_SO=$(OUT_DIR)/pjsua2.dll
+else
+  ifeq ($(OS),darwin)
+    LIBPJSUA2_SO=$(OUT_DIR)/libpjsua2.jnilib
+  else
+    ifeq ($(OS),android)
+      LIBPJSUA2_SO=android/libs/armeabi/libpjsua2.so
+    else
+      LIBPJSUA2_SO=$(OUT_DIR)/libpjsua2.so
+    endif
+  endif
+endif
+
+# Get JDK location
+ifeq ("$(JAVA_HOME)","")
+  # Get javac location to determine JDK location
+  JAVAC_PATH = $(shell which javac)
+  ifeq ("$(JAVAC_PATH)","")
+    $(error Cannot determine JDK location using 'which' command. Please define JAVA_HOME envvar)
+  endif
+
+  JAVAC_PATH := $(realpath $(JAVAC_PATH))
+  JAVA_BIN := $(dir $(JAVAC_PATH))
+  JAVA_HOME := $(patsubst %/bin/,%,$(JAVA_BIN))
+else
+  ifeq (exists, $(shell test -d $(JAVA_HOME)/bin && echo exists ))
+    JAVA_BIN := $(JAVA_HOME)/bin
+  else
+    JAVA_BIN := $(JAVA_HOME)
+  endif
+endif
+
+# OS specific
+ifeq ($(OS),Windows_NT)
+  MY_JNI_LDFLAGS	= -L$(MY_JDK)/lib -Wl,--kill-at
+else
+  MY_JNI_CFLAGS	 	= -fPIC
+  MY_JNI_LDFLAGS	= -L$(MY_JDK)/lib
+  ifeq ($(OS),darwin)
+    MY_JNI_LDFLAGS 	:= $(MY_JNI_LDFLAGS) -Wl,-soname,pjsua2.so
+  endif
+  ifeq ($(OS),android)
+    MY_JNI_CFLAGS	:= $(MY_JNI_CFLAGS) -D__ANDROID__
+  endif
+endif
+
+# Env settings, e.g: path to SWIG, JDK, java(.exe), javac(.exe)
+MY_SWIG		 = swig
+MY_JDK		 = $(JAVA_HOME)
+ifneq ($(findstring bin,$(JAVA_BIN)),)
+  MY_JAVA	 = $(MY_JDK)/bin/java
+  MY_JAVAC	 = $(MY_JDK)/bin/javac
+else
+  MY_JAVA	 = $(MY_JDK)/java
+  MY_JAVAC	 = $(MY_JDK)/javac
+endif
+MY_JNI_CFLAGS	 := $(MY_JNI_CFLAGS) -I$(MY_JDK)/include -I$(MY_JDK)/include/win32 \
+		    -I$(MY_JDK)/include/linux -I.
+
+# Build settings
+MY_CFLAGS	 = $(PJ_CFLAGS) $(MY_JNI_CFLAGS)
+MY_LDFLAGS	 = $(PJ_LDFLAGS) -lpjsua2-$(TARGET_NAME) $(PJ_LDLIBS) $(MY_JNI_LDFLAGS)
+MY_PACKAGE_NAME	 = org.pjsip.pjsua2
+ifeq ($(OS),android)
+  MY_PACKAGE_PATH  = android/src/$(subst .,/,$(MY_PACKAGE_NAME))
+else
+  MY_PACKAGE_PATH  = $(OUT_DIR)/$(subst .,/,$(MY_PACKAGE_NAME))
+endif
+
+MY_APP_JAVA = android/src/$(subst .,/,$(MY_PACKAGE_NAME))/app/MyApp.java
+
+.PHONY: all java install uninstall
+
+all: $(LIBPJSUA2_SO) java
+
+$(LIBPJSUA2_SO): $(OUT_DIR)/pjsua2_wrap.o
+	$(PJ_CXX) -shared -o $(LIBPJSUA2_SO) $(OUT_DIR)/pjsua2_wrap.o $(MY_CFLAGS) $(MY_LDFLAGS)
+
+$(OUT_DIR)/pjsua2_wrap.o: $(OUT_DIR)/pjsua2_wrap.cpp Makefile
+	$(PJ_CXX) -c $(OUT_DIR)/pjsua2_wrap.cpp -o $(OUT_DIR)/pjsua2_wrap.o $(MY_CFLAGS) $(MY_LDFLAGS)
+
+$(OUT_DIR)/pjsua2_wrap.cpp: ../pjsua2.i ../symbols.i $(SRCS)
+	mkdir -p $(MY_PACKAGE_PATH)
+	swig $(SWIG_FLAGS) -java  -package $(MY_PACKAGE_NAME) -outdir $(MY_PACKAGE_PATH) -o $(OUT_DIR)/pjsua2_wrap.cpp ../pjsua2.i
+
+clean distclean realclean:
+	rm -rf $(LIBPJSUA2_SO) $(OUT_DIR)/* $(MY_PACKAGE_PATH)/*.java $(MY_PACKAGE_PATH)/*.class
+
+java: $(MY_PACKAGE_PATH)/Error.class $(MY_PACKAGE_PATH)/test.class $(MY_PACKAGE_PATH)/sample.class
+
+$(MY_PACKAGE_PATH)/Error.class: $(MY_PACKAGE_PATH)/Error.java
+	$(MY_JAVAC) -d $(OUT_DIR) $(MY_PACKAGE_PATH)/*.java $(MY_APP_JAVA)
+
+$(MY_PACKAGE_PATH)/test.class: test.java
+	$(MY_JAVAC) -d $(OUT_DIR) -classpath "$(OUT_DIR)" test.java
+	
+$(MY_PACKAGE_PATH)/sample.class: sample.java
+	$(MY_JAVAC) -d $(OUT_DIR) -classpath "$(OUT_DIR)" sample.java
+
+test:
+	@# Need to specify classpath and library path, alternatively, they can be set via
+	@# CLASSPATH and java.library.path env settings
+	$(MY_JAVA) -cp "$(OUT_DIR)" -Djava.library.path="$(OUT_DIR)" test
+	
+sample:
+	@# Need to specify classpath and library path, alternatively, they can be set via
+	@# CLASSPATH and java.library.path env settings
+	$(MY_JAVA) -cp "$(OUT_DIR)" -Djava.library.path="$(OUT_DIR)" org.pjsip.pjsua2.app.sample	
+
+install:
+uninstall:
+
Index: /pjproject/trunk/pjsip-apps/src/swig/java/android/.classpath
===================================================================
--- /pjproject/trunk/pjsip-apps/src/swig/java/android/.classpath	(revision 4704)
+++ /pjproject/trunk/pjsip-apps/src/swig/java/android/.classpath	(revision 4704)
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
+	<classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>
+	<classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.DEPENDENCIES"/>
+	<classpathentry kind="src" path="src"/>
+	<classpathentry kind="src" path="gen"/>
+	<classpathentry kind="output" path="bin/classes"/>
+</classpath>
Index: /pjproject/trunk/pjsip-apps/src/swig/java/android/.project
===================================================================
--- /pjproject/trunk/pjsip-apps/src/swig/java/android/.project	(revision 4704)
+++ /pjproject/trunk/pjsip-apps/src/swig/java/android/.project	(revision 4704)
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>Pjsua2</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>com.android.ide.eclipse.adt.ResourceManagerBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>com.android.ide.eclipse.adt.PreCompilerBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>com.android.ide.eclipse.adt.ApkBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>com.android.ide.eclipse.adt.AndroidNature</nature>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+	</natures>
+</projectDescription>
Index: /pjproject/trunk/pjsip-apps/src/swig/java/android/.settings/org.eclipse.jdt.core.prefs
===================================================================
--- /pjproject/trunk/pjsip-apps/src/swig/java/android/.settings/org.eclipse.jdt.core.prefs	(revision 4704)
+++ /pjproject/trunk/pjsip-apps/src/swig/java/android/.settings/org.eclipse.jdt.core.prefs	(revision 4704)
@@ -0,0 +1,4 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
+org.eclipse.jdt.core.compiler.compliance=1.6
+org.eclipse.jdt.core.compiler.source=1.6
Index: /pjproject/trunk/pjsip-apps/src/swig/java/android/AndroidManifest.xml
===================================================================
--- /pjproject/trunk/pjsip-apps/src/swig/java/android/AndroidManifest.xml	(revision 4704)
+++ /pjproject/trunk/pjsip-apps/src/swig/java/android/AndroidManifest.xml	(revision 4704)
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="org.pjsip.pjsua2.app"
+    android:versionCode="1"
+    android:versionName="1.0" >
+
+    <uses-sdk
+        android:minSdkVersion="11"
+        android:targetSdkVersion="15" />
+
+    <uses-permission android:name="android.permission.INTERNET" />
+    <uses-permission android:name="android.permission.RECORD_AUDIO" />
+    <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
+    <uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS" />
+    <uses-permission android:name="android.permission.WRITE_SETTINGS" />
+    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
+    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
+    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
+    <uses-permission android:name="android.permission.WAKE_LOCK" />
+    <uses-permission android:name="android.permission.VIBRATE" />
+    <uses-permission android:name="android.permission.READ_LOGS" />
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+
+    <application
+        android:allowBackup="true"
+        android:icon="@drawable/ic_launcher"
+        android:label="@string/app_name"
+        android:theme="@style/AppTheme" >
+        <activity
+            android:name="org.pjsip.pjsua2.app.MainActivity"
+            android:label="@string/app_name" >
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+        <activity
+            android:name="org.pjsip.pjsua2.app.CallActivity"
+            android:label="@string/title_activity_call" >
+        </activity>
+    </application>
+
+</manifest>
Index: /pjproject/trunk/pjsip-apps/src/swig/java/android/jni/Android.mk
===================================================================
--- /pjproject/trunk/pjsip-apps/src/swig/java/android/jni/Android.mk	(revision 4704)
+++ /pjproject/trunk/pjsip-apps/src/swig/java/android/jni/Android.mk	(revision 4704)
@@ -0,0 +1,12 @@
+include ../../../../../build.mak
+
+LOCAL_PATH	:= $(PJDIR)/pjsip-apps/src/swig/java/android
+include $(CLEAR_VARS)
+
+LOCAL_MODULE    := libpjsua2
+LOCAL_CFLAGS    := $(APP_CFLAGS) -frtti -fexceptions
+LOCAL_LDFLAGS   := $(APP_LDFLAGS)
+LOCAL_LDLIBS    := $(APP_LDLIBS)
+LOCAL_SRC_FILES := ../output/pjsua2_wrap.cpp
+
+include $(BUILD_SHARED_LIBRARY)
Index: /pjproject/trunk/pjsip-apps/src/swig/java/android/jni/Application.mk
===================================================================
--- /pjproject/trunk/pjsip-apps/src/swig/java/android/jni/Application.mk	(revision 4704)
+++ /pjproject/trunk/pjsip-apps/src/swig/java/android/jni/Application.mk	(revision 4704)
@@ -0,0 +1,1 @@
+APP_STL := gnustl_static
Index: /pjproject/trunk/pjsip-apps/src/swig/java/android/proguard-project.txt
===================================================================
--- /pjproject/trunk/pjsip-apps/src/swig/java/android/proguard-project.txt	(revision 4704)
+++ /pjproject/trunk/pjsip-apps/src/swig/java/android/proguard-project.txt	(revision 4704)
@@ -0,0 +1,20 @@
+# To enable ProGuard in your project, edit project.properties
+# to define the proguard.config property as described in that file.
+#
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in ${sdk.dir}/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the ProGuard
+# include property in project.properties.
+#
+# For more details, see
+#   http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+#   public *;
+#}
Index: /pjproject/trunk/pjsip-apps/src/swig/java/android/project.properties
===================================================================
--- /pjproject/trunk/pjsip-apps/src/swig/java/android/project.properties	(revision 4704)
+++ /pjproject/trunk/pjsip-apps/src/swig/java/android/project.properties	(revision 4704)
@@ -0,0 +1,14 @@
+# This file is automatically generated by Android Tools.
+# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
+#
+# This file must be checked in Version Control Systems.
+#
+# To customize properties used by the Ant build system edit
+# "ant.properties", and override values to adapt the script to your
+# project structure.
+#
+# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
+#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
+
+# Project target.
+target=android-15
Index: /pjproject/trunk/pjsip-apps/src/swig/java/android/res/drawable/bkg.xml
===================================================================
--- /pjproject/trunk/pjsip-apps/src/swig/java/android/res/drawable/bkg.xml	(revision 4704)
+++ /pjproject/trunk/pjsip-apps/src/swig/java/android/res/drawable/bkg.xml	(revision 4704)
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_pressed="true" android:drawable="@color/pressed_color" />
+    <item android:drawable="@color/default_color" />
+</selector>
Index: /pjproject/trunk/pjsip-apps/src/swig/java/android/res/layout/activity_call.xml
===================================================================
--- /pjproject/trunk/pjsip-apps/src/swig/java/android/res/layout/activity_call.xml	(revision 4704)
+++ /pjproject/trunk/pjsip-apps/src/swig/java/android/res/layout/activity_call.xml	(revision 4704)
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:gravity="center"
+    android:paddingBottom="@dimen/activity_vertical_margin"
+    android:paddingLeft="@dimen/activity_horizontal_margin"
+    android:paddingRight="@dimen/activity_horizontal_margin"
+    android:paddingTop="@dimen/activity_vertical_margin"
+    android:orientation="vertical" >
+
+    <TextView
+        android:id="@+id/textViewPeer"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:gravity="center_horizontal"
+        android:text="Peer URI"
+        android:textAppearance="?android:attr/textAppearanceLarge" />
+
+    <TextView
+        android:id="@+id/textViewCallState"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:gravity="center"
+        android:text="Call state" />
+
+    <Button
+        android:id="@+id/buttonAccept"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:onClick="acceptCall"
+        android:text="Accept" />
+
+    <Button
+        android:id="@+id/buttonHangup"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:onClick="hangupCall"
+        android:text="Reject" />
+
+</LinearLayout>
Index: /pjproject/trunk/pjsip-apps/src/swig/java/android/res/layout/activity_main.xml
===================================================================
--- /pjproject/trunk/pjsip-apps/src/swig/java/android/res/layout/activity_main.xml	(revision 4704)
+++ /pjproject/trunk/pjsip-apps/src/swig/java/android/res/layout/activity_main.xml	(revision 4704)
@@ -0,0 +1,65 @@
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical"
+    android:paddingBottom="@dimen/activity_vertical_margin"
+    android:paddingLeft="@dimen/activity_horizontal_margin"
+    android:paddingRight="@dimen/activity_horizontal_margin"
+    android:paddingTop="@dimen/activity_vertical_margin"
+    tools:context=".MainActivity" >
+
+    <ListView
+        android:id="@+id/listViewBuddy"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:layout_weight="1"
+    	android:listSelector="@drawable/bkg" >
+    </ListView>
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal" >
+
+        <ImageButton
+            android:id="@+id/buttonCall"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:onClick="makeCall"
+            android:src="@android:drawable/ic_menu_call" />
+
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="match_parent"
+            android:layout_weight="1"
+			android:text="   "/>
+
+        <ImageButton
+            android:id="@+id/buttonAddBuddy"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:onClick="addBuddy"
+            android:src="@android:drawable/ic_menu_add" />
+        
+        <ImageButton
+            android:id="@+id/buttonEditBuddy"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:onClick="editBuddy"
+            android:src="@android:drawable/ic_menu_edit" />
+        
+        <ImageButton
+            android:id="@+id/buttonDelBuddy"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:onClick="delBuddy"
+            android:src="@android:drawable/ic_menu_delete" />
+
+        </LinearLayout>
+
+</LinearLayout>
Index: /pjproject/trunk/pjsip-apps/src/swig/java/android/res/layout/dlg_account_config.xml
===================================================================
--- /pjproject/trunk/pjsip-apps/src/swig/java/android/res/layout/dlg_account_config.xml	(revision 4704)
+++ /pjproject/trunk/pjsip-apps/src/swig/java/android/res/layout/dlg_account_config.xml	(revision 4704)
@@ -0,0 +1,77 @@
+<?xml version="1.0" encoding="utf-8"?>
+<TableLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:padding = "20dp"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <TextView
+        android:id="@+id/textViewInfo"
+        android:textAppearance="?android:attr/textAppearanceSmall"
+    	android:paddingBottom="20dp"
+        android:textColor="#b0b0b0" >
+    </TextView>
+    
+	<TableRow>
+        <TextView android:text="ID">
+        </TextView>  
+
+	    <EditText
+	        android:id="@+id/editTextId"
+	        android:layout_width="match_parent"
+	        android:layout_height="wrap_content"
+	        android:layout_weight="1"
+	        android:inputType="textUri" >
+
+	        <requestFocus />
+	    </EditText>
+    </TableRow>
+	<TableRow>
+        <TextView android:text="Registrar">
+        </TextView>  
+	    <EditText
+	        android:id="@+id/editTextRegistrar"
+	        android:layout_width="match_parent"
+	        android:layout_height="wrap_content"
+	        android:layout_weight="1"
+	        android:inputType="textUri" >
+	    </EditText>
+    </TableRow>
+	<TableRow>
+        <TextView android:text="Proxy">
+        </TextView>  
+	    <EditText
+	        android:id="@+id/editTextProxy"
+	        android:layout_width="match_parent"
+	        android:layout_height="wrap_content"
+	        android:layout_weight="1"
+	        android:inputType="textUri" >
+	    </EditText>
+    </TableRow>
+	<TableRow>
+        <TextView android:text="Username">
+        </TextView>  
+
+	    <EditText
+	        android:id="@+id/editTextUsername"
+	        android:layout_width="match_parent"
+	        android:layout_height="wrap_content"
+	        android:layout_weight="1"
+	        android:inputType="text" >
+
+	    </EditText>
+    </TableRow>
+	<TableRow>
+        <TextView android:text="Password">
+        </TextView>  
+
+	    <EditText
+	        android:id="@+id/editTextPassword"
+	        android:layout_width="match_parent"
+	        android:layout_height="wrap_content"
+	        android:layout_weight="1"
+	        android:inputType="textPassword" >
+
+	    </EditText>
+    </TableRow>
+</TableLayout>
Index: /pjproject/trunk/pjsip-apps/src/swig/java/android/res/layout/dlg_add_buddy.xml
===================================================================
--- /pjproject/trunk/pjsip-apps/src/swig/java/android/res/layout/dlg_add_buddy.xml	(revision 4704)
+++ /pjproject/trunk/pjsip-apps/src/swig/java/android/res/layout/dlg_add_buddy.xml	(revision 4704)
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<TableLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:padding = "20dp"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+	<TableRow>
+        <TextView android:text="Buddy URI">
+        </TextView>  
+
+	    <EditText
+	        android:id="@+id/editTextUri"
+	        android:layout_weight="1"
+	        android:inputType="textUri" >
+	        <requestFocus />
+	    </EditText>
+    </TableRow>
+	<TableRow>
+        <CheckBox
+            android:id="@+id/checkBoxSubscribe"
+            android:layout_column="1"
+            android:text="Subscribe presence" />
+    </TableRow>
+</TableLayout>
Index: /pjproject/trunk/pjsip-apps/src/swig/java/android/res/menu/call.xml
===================================================================
--- /pjproject/trunk/pjsip-apps/src/swig/java/android/res/menu/call.xml	(revision 4704)
+++ /pjproject/trunk/pjsip-apps/src/swig/java/android/res/menu/call.xml	(revision 4704)
@@ -0,0 +1,9 @@
+<menu xmlns:android="http://schemas.android.com/apk/res/android" >
+
+    <item
+        android:id="@+id/action_settings"
+        android:orderInCategory="100"
+        android:showAsAction="never"
+        android:title="@string/action_settings"/>
+
+</menu>
Index: /pjproject/trunk/pjsip-apps/src/swig/java/android/res/menu/main.xml
===================================================================
--- /pjproject/trunk/pjsip-apps/src/swig/java/android/res/menu/main.xml	(revision 4704)
+++ /pjproject/trunk/pjsip-apps/src/swig/java/android/res/menu/main.xml	(revision 4704)
@@ -0,0 +1,14 @@
+<menu xmlns:android="http://schemas.android.com/apk/res/android" >
+
+    <item
+        android:id="@+id/action_acc_config"
+        android:icon="@android:drawable/ic_menu_manage"
+        android:showAsAction="ifRoom"
+        android:title="Account Config"/>
+    <item
+        android:id="@+id/action_quit"
+        android:icon="@android:drawable/ic_menu_close_clear_cancel"
+        android:showAsAction="ifRoom"
+        android:title="Quit"/>
+    
+</menu>
Index: /pjproject/trunk/pjsip-apps/src/swig/java/android/res/values-sw600dp/dimens.xml
===================================================================
--- /pjproject/trunk/pjsip-apps/src/swig/java/android/res/values-sw600dp/dimens.xml	(revision 4704)
+++ /pjproject/trunk/pjsip-apps/src/swig/java/android/res/values-sw600dp/dimens.xml	(revision 4704)
@@ -0,0 +1,8 @@
+<resources>
+
+    <!--
+         Customize dimensions originally defined in res/values/dimens.xml (such as
+         screen margins) for sw600dp devices (e.g. 7" tablets) here.
+    -->
+
+</resources>
Index: /pjproject/trunk/pjsip-apps/src/swig/java/android/res/values-sw720dp-land/dimens.xml
===================================================================
--- /pjproject/trunk/pjsip-apps/src/swig/java/android/res/values-sw720dp-land/dimens.xml	(revision 4704)
+++ /pjproject/trunk/pjsip-apps/src/swig/java/android/res/values-sw720dp-land/dimens.xml	(revision 4704)
@@ -0,0 +1,9 @@
+<resources>
+
+    <!--
+         Customize dimensions originally defined in res/values/dimens.xml (such as
+         screen margins) for sw720dp devices (e.g. 10" tablets) in landscape here.
+    -->
+    <dimen name="activity_horizontal_margin">128dp</dimen>
+
+</resources>
Index: /pjproject/trunk/pjsip-apps/src/swig/java/android/res/values-v11/styles.xml
===================================================================
--- /pjproject/trunk/pjsip-apps/src/swig/java/android/res/values-v11/styles.xml	(revision 4704)
+++ /pjproject/trunk/pjsip-apps/src/swig/java/android/res/values-v11/styles.xml	(revision 4704)
@@ -0,0 +1,11 @@
+<resources>
+
+    <!--
+        Base application theme for API 11+. This theme completely replaces
+        AppBaseTheme from res/values/styles.xml on API 11+ devices.
+    -->
+    <style name="AppBaseTheme" parent="android:Theme.Holo.Light">
+        <!-- API 11 theme customizations can go here. -->
+    </style>
+
+</resources>
Index: /pjproject/trunk/pjsip-apps/src/swig/java/android/res/values-v14/styles.xml
===================================================================
--- /pjproject/trunk/pjsip-apps/src/swig/java/android/res/values-v14/styles.xml	(revision 4704)
+++ /pjproject/trunk/pjsip-apps/src/swig/java/android/res/values-v14/styles.xml	(revision 4704)
@@ -0,0 +1,12 @@
+<resources>
+
+    <!--
+        Base application theme for API 14+. This theme completely replaces
+        AppBaseTheme from BOTH res/values/styles.xml and
+        res/values-v11/styles.xml on API 14+ devices.
+    -->
+    <style name="AppBaseTheme" parent="android:Theme.Holo.Light.DarkActionBar">
+        <!-- API 14 theme customizations can go here. -->
+    </style>
+
+</resources>
Index: /pjproject/trunk/pjsip-apps/src/swig/java/android/res/values/colors.xml
===================================================================
--- /pjproject/trunk/pjsip-apps/src/swig/java/android/res/values/colors.xml	(revision 4704)
+++ /pjproject/trunk/pjsip-apps/src/swig/java/android/res/values/colors.xml	(revision 4704)
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <color name="pressed_color">#B8F2F5</color>
+    <color name="default_color">#E8FEFF</color>
+</resources>
Index: /pjproject/trunk/pjsip-apps/src/swig/java/android/res/values/dimens.xml
===================================================================
--- /pjproject/trunk/pjsip-apps/src/swig/java/android/res/values/dimens.xml	(revision 4704)
+++ /pjproject/trunk/pjsip-apps/src/swig/java/android/res/values/dimens.xml	(revision 4704)
@@ -0,0 +1,7 @@
+<resources>
+
+    <!-- Default screen margins, per the Android Design guidelines. -->
+    <dimen name="activity_horizontal_margin">16dp</dimen>
+    <dimen name="activity_vertical_margin">16dp</dimen>
+
+</resources>
Index: /pjproject/trunk/pjsip-apps/src/swig/java/android/res/values/strings.xml
===================================================================
--- /pjproject/trunk/pjsip-apps/src/swig/java/android/res/values/strings.xml	(revision 4704)
+++ /pjproject/trunk/pjsip-apps/src/swig/java/android/res/values/strings.xml	(revision 4704)
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+
+    <string name="app_name">Pjsua2</string>
+    <string name="action_settings">Settings</string>
+    <string name="title_activity_call">Call</string>
+    <string name="hello_world">Hello world!</string>
+
+</resources>
Index: /pjproject/trunk/pjsip-apps/src/swig/java/android/res/values/styles.xml
===================================================================
--- /pjproject/trunk/pjsip-apps/src/swig/java/android/res/values/styles.xml	(revision 4704)
+++ /pjproject/trunk/pjsip-apps/src/swig/java/android/res/values/styles.xml	(revision 4704)
@@ -0,0 +1,20 @@
+<resources>
+
+    <!--
+        Base application theme, dependent on API level. This theme is replaced
+        by AppBaseTheme from res/values-vXX/styles.xml on newer devices.
+    -->
+    <style name="AppBaseTheme" parent="android:Theme.Light">
+        <!--
+            Theme customizations available in newer API levels can go in
+            res/values-vXX/styles.xml, while customizations related to
+            backward-compatibility can go here.
+        -->
+    </style>
+
+    <!-- Application theme. -->
+    <style name="AppTheme" parent="AppBaseTheme">
+        <!-- All customizations that are NOT specific to a particular API-level can go here. -->
+    </style>
+
+</resources>
Index: /pjproject/trunk/pjsip-apps/src/swig/java/android/src/org/pjsip/pjsua2/app/CallActivity.java
===================================================================
--- /pjproject/trunk/pjsip-apps/src/swig/java/android/src/org/pjsip/pjsua2/app/CallActivity.java	(revision 4704)
+++ /pjproject/trunk/pjsip-apps/src/swig/java/android/src/org/pjsip/pjsua2/app/CallActivity.java	(revision 4704)
@@ -0,0 +1,146 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2013 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
+ */
+package org.pjsip.pjsua2.app;
+
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.view.View;
+import android.widget.Button;
+import android.widget.TextView;
+import android.app.Activity;
+
+import org.pjsip.pjsua2.*;
+
+public class CallActivity extends Activity implements Handler.Callback {
+	
+	public static Handler handler_;
+
+	private final Handler handler = new Handler(this);
+	private static CallInfo lastCallInfo;
+
+	@Override
+	protected void onCreate(Bundle savedInstanceState) {
+		super.onCreate(savedInstanceState);
+		setContentView(R.layout.activity_call);
+		
+		handler_ = handler;
+		if (MainActivity.currentCall != null) {
+			try {
+				lastCallInfo = MainActivity.currentCall.getInfo();
+				updateCallState(lastCallInfo);
+			} catch (Exception e) {
+				System.out.println(e);
+			}
+		} else {
+			updateCallState(lastCallInfo);
+		}
+	}
+
+    @Override
+    protected void onDestroy() {
+    	super.onDestroy();
+    	handler_ = null;
+    }
+	
+	public void acceptCall(View view) {
+		CallOpParam prm = new CallOpParam();
+		prm.setStatusCode(pjsip_status_code.PJSIP_SC_OK);
+		try {
+			MainActivity.currentCall.answer(prm);
+		} catch (Exception e) {
+			System.out.println(e);
+		}
+		
+		view.setVisibility(View.GONE);
+	}
+
+	public void hangupCall(View view) {
+		handler_ = null;
+		finish();
+		
+		if (MainActivity.currentCall != null) {
+			CallOpParam prm = new CallOpParam();
+			prm.setStatusCode(pjsip_status_code.PJSIP_SC_DECLINE);
+			try {
+				MainActivity.currentCall.hangup(prm);
+			} catch (Exception e) {
+				System.out.println(e);
+			}
+
+			MainActivity.currentCall = null;
+		}
+	}
+	
+	@Override
+	public boolean handleMessage(Message m) {
+		
+		if (m.what == MainActivity.MSG_TYPE.CALL_STATE) {
+			
+			lastCallInfo = (CallInfo) m.obj;
+			updateCallState(lastCallInfo);
+			
+		} else {
+			
+			/* Message not handled */
+			return false;
+			
+		}
+			
+		return true;
+	}
+	
+	private void updateCallState(CallInfo ci) {
+		TextView tvPeer  = (TextView) findViewById(R.id.textViewPeer);
+		TextView tvState = (TextView) findViewById(R.id.textViewCallState);
+		Button buttonHangup = (Button) findViewById(R.id.buttonHangup);
+		Button buttonAccept = (Button) findViewById(R.id.buttonAccept);
+		String call_state = "";
+		
+		if (ci.getRole() == pjsip_role_e.PJSIP_ROLE_UAC) {
+			buttonAccept.setVisibility(View.GONE);
+		}
+				
+		if (ci.getState().swigValue() < pjsip_inv_state.PJSIP_INV_STATE_CONFIRMED.swigValue())
+		{
+			if (ci.getRole() == pjsip_role_e.PJSIP_ROLE_UAS) {
+				call_state = "Incoming call..";
+				/* Default button texts are already 'Accept' & 'Reject' */
+			} else {
+				buttonHangup.setText("Cancel");
+				call_state = ci.getStateText();
+			}
+		}
+		else if (ci.getState().swigValue() >= pjsip_inv_state.PJSIP_INV_STATE_CONFIRMED.swigValue())
+		{
+			buttonAccept.setVisibility(View.GONE);
+			call_state = ci.getStateText();
+			if (ci.getState() == pjsip_inv_state.PJSIP_INV_STATE_CONFIRMED) {
+				buttonHangup.setText("Hangup");
+			} else if (ci.getState() == pjsip_inv_state.PJSIP_INV_STATE_DISCONNECTED) {
+				buttonHangup.setText("OK");
+				call_state = "Call disconnected: " + ci.getLastReason();
+				MainActivity.currentCall = null;
+			}
+		}
+		
+		tvPeer.setText(ci.getRemoteUri());
+		tvState.setText(call_state);
+	}
+}
Index: /pjproject/trunk/pjsip-apps/src/swig/java/android/src/org/pjsip/pjsua2/app/MainActivity.java
===================================================================
--- /pjproject/trunk/pjsip-apps/src/swig/java/android/src/org/pjsip/pjsua2/app/MainActivity.java	(revision 4704)
+++ /pjproject/trunk/pjsip-apps/src/swig/java/android/src/org/pjsip/pjsua2/app/MainActivity.java	(revision 4704)
@@ -0,0 +1,497 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2013 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
+ */
+package org.pjsip.pjsua2.app;
+
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.CheckBox;
+import android.widget.EditText;
+import android.widget.ListView;
+import android.widget.SimpleAdapter;
+import android.widget.TextView;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.pjsip.pjsua2.*;
+
+public class MainActivity extends Activity implements Handler.Callback, MyAppObserver {
+	public static MyApp app = null;
+	public static MyCall currentCall = null;
+	public static MyAccount account = null;
+	public static AccountConfig accCfg = null;
+	
+	private ListView buddyListView;
+	private SimpleAdapter buddyListAdapter;
+	private int buddyListSelectedIdx = -1;
+    ArrayList<Map<String, String>> buddyList;
+    private String lastRegStatus = "";
+
+	private final Handler handler = new Handler(this);
+	public class MSG_TYPE {
+		public final static int INCOMING_CALL = 1;
+		public final static int CALL_STATE = 2;
+		public final static int REG_STATE = 3;
+		public final static int BUDDY_STATE = 4;
+	}
+
+	private HashMap<String, String> putData(String uri, String status) {
+		HashMap<String, String> item = new HashMap<String, String>();
+		item.put("uri", uri);
+		item.put("status", status);
+		return item;
+	}
+	
+	private void showCallActivity() {
+        Intent intent = new Intent(this, CallActivity.class);
+        intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+        startActivity(intent);
+	}
+
+	@Override
+	protected void onCreate(Bundle savedInstanceState) {
+		super.onCreate(savedInstanceState);
+		setContentView(R.layout.activity_main);
+		
+		if (app == null) {
+			app = new MyApp();
+			/* Wait for GDB to init */
+			if ((getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) {
+				try {
+					Thread.sleep(5000);
+				} catch (InterruptedException e) {}
+			}
+			
+		    app.init(this, getFilesDir().getAbsolutePath());
+		}
+		
+	    if (app.accList.size() == 0) {
+	    	accCfg = new AccountConfig();
+	    	accCfg.setIdUri("sip:localhost");
+	    	account = app.addAcc(accCfg);
+	    } else {
+	    	account = app.accList.get(0);
+	    	accCfg = account.cfg;
+	    }
+	    
+		buddyList = new ArrayList<Map<String, String>>();
+		for (int i = 0; i < account.buddyList.size(); i++) {
+			buddyList.add(putData(account.buddyList.get(i).cfg.getUri(),
+								  account.buddyList.get(i).getStatusText()));
+		}
+
+		String[] from = { "uri", "status" };
+	    int[] to = { android.R.id.text1, android.R.id.text2 };
+	    buddyListAdapter = new SimpleAdapter(this, buddyList, android.R.layout.simple_list_item_2, from, to);
+	    
+		buddyListView = (ListView) findViewById(R.id.listViewBuddy);;
+	    buddyListView.setAdapter(buddyListAdapter);
+	    buddyListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
+	    	@Override
+	    	public void onItemClick(AdapterView<?> parent, final View view,
+	    			int position, long id) 
+	    	{
+	    		view.setSelected(true);
+	    		buddyListSelectedIdx = position;
+	    	}
+	    });
+	    
+	}
+
+	@Override
+	public boolean onCreateOptionsMenu(Menu menu) {
+		// Inflate the menu; this adds items to the action bar if it is present.
+		getMenuInflater().inflate(R.menu.main, menu);
+		return true;
+	}
+
+	@Override
+	public boolean onOptionsItemSelected(MenuItem item) {
+		switch (item.getItemId()) {
+		case R.id.action_acc_config:
+			dlgAccountSetting();
+			break;
+
+		case R.id.action_quit:
+			Message m = Message.obtain(handler, 0);
+			m.sendToTarget();
+			break;
+			
+		default:
+			break;
+		}
+
+		return true;
+	} 	
+
+	@Override
+	public boolean handleMessage(Message m) {
+		
+		if (m.what == 0) {
+			
+			app.deinit();
+			finish();
+			Runtime.getRuntime().gc();
+			android.os.Process.killProcess(android.os.Process.myPid());
+			
+		} else if (m.what == MSG_TYPE.CALL_STATE) {
+			
+			CallInfo ci = (CallInfo) m.obj;
+			
+			/* Forward the message to CallActivity */
+			if (CallActivity.handler_ != null) {
+				Message m2 = Message.obtain(CallActivity.handler_, MSG_TYPE.CALL_STATE, ci);
+				m2.sendToTarget();
+			}
+			
+			if (ci.getState() == pjsip_inv_state.PJSIP_INV_STATE_DISCONNECTED)
+				currentCall = null;
+			
+		} else if (m.what == MSG_TYPE.BUDDY_STATE) {
+			
+			MyBuddy buddy = (MyBuddy) m.obj;
+			int idx = account.buddyList.indexOf(buddy);
+			if (idx >= 0) {
+				buddyList.get(idx).put("status", buddy.getStatusText());
+				buddyListAdapter.notifyDataSetChanged();
+				// TODO: selection color/mark is gone after this,
+				//       dont know how to return it back.
+				//buddyListView.setSelection(buddyListSelectedIdx);
+				//buddyListView.performItemClick(buddyListView, buddyListSelectedIdx,
+				//							   buddyListView.getItemIdAtPosition(buddyListSelectedIdx));
+				
+				/* Return back Call activity */
+				notifyCallState(currentCall);
+			}
+			
+		} else if (m.what == MSG_TYPE.REG_STATE) {
+			
+			String msg_str = (String) m.obj;
+			lastRegStatus = msg_str;
+			
+		} else if (m.what == MSG_TYPE.INCOMING_CALL) {
+			
+			/* Incoming call */
+			final MyCall call = (MyCall) m.obj;
+			CallOpParam prm = new CallOpParam();
+			
+			/* Only one call at anytime */
+			if (currentCall != null) {
+				prm.setStatusCode(pjsip_status_code.PJSIP_SC_BUSY_HERE);
+				try {
+					call.hangup(prm);
+				} catch (Exception e) {}
+				return true;
+			}
+
+			/* Answer with ringing */
+			prm.setStatusCode(pjsip_status_code.PJSIP_SC_RINGING);
+			try {
+				call.answer(prm);
+			} catch (Exception e) {}
+			
+			currentCall = call;
+			showCallActivity();
+
+		} else {
+			
+			/* Message not handled */
+			return false;
+			
+		}
+			
+		return true;
+	}
+	
+
+	private void dlgAccountSetting() {
+		
+		LayoutInflater li = LayoutInflater.from(this);
+		View view = li.inflate(R.layout.dlg_account_config, null);
+		
+		if (!lastRegStatus.isEmpty()) {
+			TextView tvInfo = (TextView)view.findViewById(R.id.textViewInfo);
+			tvInfo.setText("Last status: " + lastRegStatus);
+		}
+
+		AlertDialog.Builder adb = new AlertDialog.Builder(this);
+		adb.setView(view);
+		adb.setTitle("Account Settings");
+
+		final EditText etId    = (EditText)view.findViewById(R.id.editTextId);
+		final EditText etReg   = (EditText)view.findViewById(R.id.editTextRegistrar);
+		final EditText etProxy = (EditText)view.findViewById(R.id.editTextProxy);
+		final EditText etUser  = (EditText)view.findViewById(R.id.editTextUsername);
+		final EditText etPass  = (EditText)view.findViewById(R.id.editTextPassword);
+		
+		etId.   setText(accCfg.getIdUri());
+		etReg.  setText(accCfg.getRegConfig().getRegistrarUri());
+		StringVector proxies = accCfg.getSipConfig().getProxies();
+		if (proxies.size() > 0)
+			etProxy.setText(proxies.get(0));
+		else
+			etProxy.setText("");
+		AuthCredInfoVector creds = accCfg.getSipConfig().getAuthCreds();
+		if (creds.size() > 0) {
+			etUser. setText(creds.get(0).getUsername());
+			etPass. setText(creds.get(0).getData());
+		} else {
+			etUser. setText("");
+			etPass. setText("");
+		}
+		
+		adb.setCancelable(false);
+		adb.setPositiveButton("OK",
+			  new DialogInterface.OnClickListener() {
+			    public void onClick(DialogInterface dialog,int id) {
+			    	String acc_id 	 = etId.getText().toString();
+			    	String registrar = etReg.getText().toString();
+			    	String proxy 	 = etProxy.getText().toString();
+			    	String username  = etUser.getText().toString();
+			    	String password  = etPass.getText().toString();
+			    	
+			    	accCfg.setIdUri(acc_id);
+			    	accCfg.getRegConfig().setRegistrarUri(registrar);
+					AuthCredInfoVector creds = accCfg.getSipConfig().getAuthCreds();
+					creds.clear();
+					if (!username.isEmpty()) {
+						creds.add(new AuthCredInfo("Digest", "*", username, 0, password));
+					}
+					StringVector proxies = accCfg.getSipConfig().getProxies();
+					proxies.clear();
+					if (!proxy.isEmpty()) {
+						proxies.add(proxy);
+					}
+					
+					/* Finally */
+					lastRegStatus = "";
+					try {
+						account.modify(accCfg);
+					} catch (Exception e) {}
+			    }
+			  });
+		adb.setNegativeButton("Cancel",
+			  new DialogInterface.OnClickListener() {
+			    public void onClick(DialogInterface dialog,int id) {
+				dialog.cancel();
+			    }
+			  });
+
+		AlertDialog ad = adb.create();
+		ad.show();
+	}
+	
+	
+	public void makeCall(View view) {
+		if (buddyListSelectedIdx == -1)
+			return;
+		
+		/* Only one call at anytime */
+		if (currentCall != null) {
+			return;
+		}
+		
+		HashMap<String, String> item = (HashMap<String, String>) buddyListView.getItemAtPosition(buddyListSelectedIdx);
+		String buddy_uri = item.get("uri");
+		
+		MyCall call = new MyCall(account, -1);
+		CallOpParam prm = new CallOpParam();
+		CallSetting opt = prm.getOpt();
+		opt.setAudioCount(1);
+		opt.setVideoCount(0);
+
+		try {
+			call.makeCall(buddy_uri, prm);
+		} catch (Exception e) {
+			currentCall = null;
+			return;
+		}
+		
+		currentCall = call;
+		showCallActivity();
+	}
+
+	private void dlgAddEditBuddy(BuddyConfig initial) {
+		final BuddyConfig cfg = new BuddyConfig();
+		final BuddyConfig old_cfg = initial;
+		final boolean is_add = initial == null;
+		
+		LayoutInflater li = LayoutInflater.from(this);
+		View view = li.inflate(R.layout.dlg_add_buddy, null);
+
+		AlertDialog.Builder adb = new AlertDialog.Builder(this);
+		adb.setView(view);
+
+		final EditText etUri    = (EditText)view.findViewById(R.id.editTextUri);
+		final CheckBox cbSubs  = (CheckBox)view.findViewById(R.id.checkBoxSubscribe);
+
+		if (is_add) {
+			adb.setTitle("Add Buddy");
+		} else {
+			adb.setTitle("Edit Buddy");
+			etUri. setText(initial.getUri());
+			cbSubs.setChecked(initial.getSubscribe());
+		}
+
+		adb.setCancelable(false);
+		adb.setPositiveButton("OK",
+			  new DialogInterface.OnClickListener() {
+			    public void onClick(DialogInterface dialog,int id) {
+			    	cfg.setUri(etUri.getText().toString());
+			    	cfg.setSubscribe(cbSubs.isChecked());
+			    	
+			    	if (is_add) {
+			    		account.addBuddy(cfg);
+						buddyList.add(putData(cfg.getUri(), ""));
+						buddyListAdapter.notifyDataSetChanged();
+						buddyListSelectedIdx = -1;
+			    	} else {
+			    		if (!old_cfg.getUri().equals(cfg.getUri())) {
+			    			account.delBuddy(buddyListSelectedIdx);
+			    			account.addBuddy(cfg);
+							buddyList.remove(buddyListSelectedIdx);
+							buddyList.add(putData(cfg.getUri(), ""));
+			    			buddyListAdapter.notifyDataSetChanged();
+			    			buddyListSelectedIdx = -1;
+			    		} else if (old_cfg.getSubscribe() != cfg.getSubscribe()) {
+			    			MyBuddy bud = account.buddyList.get(buddyListSelectedIdx);
+							try {
+				    			bud.subscribePresence(cfg.getSubscribe());
+							} catch (Exception e) {}
+			    		}
+			    	}
+			    }
+			  });
+		adb.setNegativeButton("Cancel",
+			  new DialogInterface.OnClickListener() {
+			    public void onClick(DialogInterface dialog,int id) {
+				dialog.cancel();
+			    }
+			  });
+
+		AlertDialog ad = adb.create();
+		ad.show();
+	}
+	
+	public void addBuddy(View view) {
+		dlgAddEditBuddy(null);
+	}
+
+	public void editBuddy(View view) {
+		if (buddyListSelectedIdx == -1)
+			return;
+		
+		BuddyConfig old_cfg = account.buddyList.get(buddyListSelectedIdx).cfg;
+		dlgAddEditBuddy(old_cfg);
+	}
+	
+	public void delBuddy(View view) {
+		if (buddyListSelectedIdx == -1)
+			return;
+		
+		final HashMap<String, String> item = (HashMap<String, String>) buddyListView.getItemAtPosition(buddyListSelectedIdx);
+		String buddy_uri = item.get("uri");
+		
+		DialogInterface.OnClickListener ocl = new DialogInterface.OnClickListener() {
+		    @Override
+		    public void onClick(DialogInterface dialog, int which) {
+		        switch (which) {
+		        case DialogInterface.BUTTON_POSITIVE:
+		        	account.delBuddy(buddyListSelectedIdx);
+		    		buddyList.remove(item);
+		    		buddyListAdapter.notifyDataSetChanged();
+		    		buddyListSelectedIdx = -1;
+		            break;
+		        case DialogInterface.BUTTON_NEGATIVE:
+		            break;
+		        }
+		    }
+		};
+
+		AlertDialog.Builder adb = new AlertDialog.Builder(this);
+		adb.setTitle(buddy_uri);
+		adb.setMessage("\nDelete this buddy?\n");
+		adb.setPositiveButton("Yes", ocl);
+		adb.setNegativeButton("No", ocl);
+		adb.show();
+	}
+	
+	
+	/*
+	 * === MyAppObserver ===
+	 * 
+	 * As we cannot do UI from worker thread, the callbacks mostly just send
+	 * a message to UI/main thread.
+	 */
+	
+	public void notifyIncomingCall(MyCall call) {
+		Message m = Message.obtain(handler, MSG_TYPE.INCOMING_CALL, call);
+		m.sendToTarget();
+	}
+
+	public void notifyRegState(pjsip_status_code code, String reason, int expiration) {
+		String msg_str = "";
+		if (expiration == 0)
+			msg_str += "Unregistration";
+		else
+			msg_str += "Registration";
+		
+		if (code.swigValue()/100 == 2)
+			msg_str += " successful";
+		else
+			msg_str += " failed: " + reason;
+		
+		Message m = Message.obtain(handler, MSG_TYPE.REG_STATE, msg_str);
+		m.sendToTarget();
+	}
+	
+	public void notifyCallState(MyCall call) {
+		if (currentCall == null || call.getId() != currentCall.getId())
+			return;
+		
+		CallInfo ci;
+		try {
+			ci = call.getInfo();
+		} catch (Exception e) {
+			ci = null;
+		}
+		Message m = Message.obtain(handler, MSG_TYPE.CALL_STATE, ci);
+		m.sendToTarget();
+	}
+	
+	public void notifyBuddyState(MyBuddy buddy) {
+		Message m = Message.obtain(handler, MSG_TYPE.BUDDY_STATE, buddy);
+		m.sendToTarget();
+	}
+
+	/* === end of MyAppObserver ==== */
+
+}
Index: /pjproject/trunk/pjsip-apps/src/swig/java/android/src/org/pjsip/pjsua2/app/MyApp.java
===================================================================
--- /pjproject/trunk/pjsip-apps/src/swig/java/android/src/org/pjsip/pjsua2/app/MyApp.java	(revision 4704)
+++ /pjproject/trunk/pjsip-apps/src/swig/java/android/src/org/pjsip/pjsua2/app/MyApp.java	(revision 4704)
@@ -0,0 +1,449 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2013 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
+ */
+package org.pjsip.pjsua2.app;
+
+import java.io.File;
+import java.util.ArrayList;
+import org.pjsip.pjsua2.*;
+
+
+/* Interface to separate UI & engine a bit better */
+interface MyAppObserver {
+	abstract void notifyRegState(pjsip_status_code code, String reason, int expiration);
+	abstract void notifyIncomingCall(MyCall call);
+	abstract void notifyCallState(MyCall call);
+	abstract void notifyBuddyState(MyBuddy buddy);
+}
+
+
+class MyLogWriter extends LogWriter {
+	@Override
+	public void write(LogEntry entry) {
+		System.out.println(entry.getMsg());
+	}
+}
+
+
+class MyCall extends Call {
+	MyCall(MyAccount acc, int call_id) {
+		super(acc, call_id);
+	}
+
+	@Override
+	public void onCallState(OnCallStateParam prm) {
+		MyApp.observer.notifyCallState(this);
+	}
+	
+	@Override
+	public void onCallMediaState(OnCallMediaStateParam prm) {
+		CallInfo ci;
+		try {
+			ci = getInfo();
+		} catch (Exception e) {
+			return;
+		}
+		
+		CallMediaInfoVector cmiv = ci.getMedia();
+		
+		for (int i = 0; i < cmiv.size(); i++) {
+			CallMediaInfo cmi = cmiv.get(i);
+			if (cmi.getType() == pjmedia_type.PJMEDIA_TYPE_AUDIO &&
+			    (cmi.getStatus() == pjsua_call_media_status.PJSUA_CALL_MEDIA_ACTIVE ||
+			     cmi.getStatus() == pjsua_call_media_status.PJSUA_CALL_MEDIA_REMOTE_HOLD))
+			{
+				// unfortunately, on Java too, the returned Media cannot be downcasted to AudioMedia 
+				Media m = getMedia(i);
+				AudioMedia am = AudioMedia.typecastFromMedia(m);
+				
+				// connect ports
+				try {
+					MyApp.ep.audDevManager().getCaptureDevMedia().startTransmit(am);
+					am.startTransmit(MyApp.ep.audDevManager().getPlaybackDevMedia());
+				} catch (Exception e) {
+					continue;
+				}
+			}
+		}
+	}
+}
+
+
+class MyAccount extends Account {
+	public ArrayList<MyBuddy> buddyList = new ArrayList<MyBuddy>();
+	public AccountConfig cfg;
+	
+	MyAccount(AccountConfig config) {
+		super();
+		cfg = config;
+	}
+	
+	public MyBuddy addBuddy(BuddyConfig bud_cfg)
+	{
+		/* Create Buddy */
+		MyBuddy bud = new MyBuddy(bud_cfg);
+		try {
+			bud.create(this, bud_cfg);
+		} catch (Exception e) {
+			bud = null;
+		}
+		
+		if (bud != null) {
+			buddyList.add(bud);
+			if (bud_cfg.getSubscribe())
+				try {
+					bud.subscribePresence(true);
+				} catch (Exception e) {}
+		}
+		
+		return bud;
+	}
+	
+	public void delBuddy(MyBuddy buddy) {
+		buddyList.remove(buddy);
+	}
+	
+	public void delBuddy(int index) {
+		buddyList.remove(index);
+	}
+	
+	@Override
+	public void onRegState(OnRegStateParam prm) {
+		MyApp.observer.notifyRegState(prm.getCode(), prm.getReason(), prm.getExpiration());
+	}
+
+	@Override
+	public void onIncomingCall(OnIncomingCallParam prm) {
+		System.out.println("======== Incoming call ======== ");
+		MyCall call = new MyCall(this, prm.getCallId());
+		MyApp.observer.notifyIncomingCall(call);
+	}
+	
+	@Override
+	public void onInstantMessage(OnInstantMessageParam prm) {
+		System.out.println("======== Incoming pager ======== ");
+		System.out.println("From 		: " + prm.getFromUri());
+		System.out.println("To			: " + prm.getToUri());
+		System.out.println("Contact		: " + prm.getContactUri());
+		System.out.println("Mimetype	: " + prm.getContentType());
+		System.out.println("Body		: " + prm.getMsgBody());
+	}
+}
+
+
+class MyBuddy extends Buddy {
+	public BuddyConfig cfg;
+	
+	MyBuddy(BuddyConfig config) {
+		super();
+		cfg = config;
+	}
+	
+	String getStatusText() {
+		BuddyInfo bi;
+		
+		try {
+			bi = getInfo();
+		} catch (Exception e) {
+			return "?";
+		}
+		
+		String status = "";
+		if (bi.getSubState() == pjsip_evsub_state.PJSIP_EVSUB_STATE_ACTIVE) {
+			if (bi.getPresStatus().getStatus() == pjsua_buddy_status.PJSUA_BUDDY_STATUS_ONLINE) {
+				status = bi.getPresStatus().getStatusText();
+				if (status == null || status.isEmpty()) {
+					status = "Online";
+				}
+			} else if (bi.getPresStatus().getStatus() == pjsua_buddy_status.PJSUA_BUDDY_STATUS_OFFLINE) {
+				status = "Offline";
+			} else {
+				status = "Unknown";
+			}
+		}
+		return status;
+	}
+
+	@Override
+	public void onBuddyState() {
+		MyApp.observer.notifyBuddyState(this);
+	}
+	
+}
+
+
+class MyAccountConfig {
+	public AccountConfig accCfg = new AccountConfig();
+	public ArrayList<BuddyConfig> buddyCfgs = new ArrayList<BuddyConfig>();
+	
+	public void readObject(ContainerNode node) {
+		try {
+			ContainerNode acc_node = node.readContainer("Account");
+			accCfg.readObject(acc_node);
+			ContainerNode buddies_node = acc_node.readArray("buddies");
+			buddyCfgs.clear();
+			while (buddies_node.hasUnread()) {
+				BuddyConfig bud_cfg = new BuddyConfig(); 
+				bud_cfg.readObject(buddies_node);
+				buddyCfgs.add(bud_cfg);
+			}
+		} catch (Exception e) {}
+	}
+	
+	public void writeObject(ContainerNode node) {
+		try {
+			ContainerNode acc_node = node.writeNewContainer("Account");
+			accCfg.writeObject(acc_node);
+			ContainerNode buddies_node = acc_node.writeNewArray("buddies");
+			for (int j = 0; j < buddyCfgs.size(); j++) {
+				buddyCfgs.get(j).writeObject(buddies_node);
+			}
+		} catch (Exception e) {}
+	}
+}
+
+
+class MyApp {
+	static {
+		System.loadLibrary("pjsua2");
+		System.out.println("Library loaded");
+	}
+	
+	public static Endpoint ep = new Endpoint();
+	public static MyAppObserver observer;
+	public ArrayList<MyAccount> accList = new ArrayList<MyAccount>();
+
+	private ArrayList<MyAccountConfig> accCfgs = new ArrayList<MyAccountConfig>();
+	private EpConfig epConfig = new EpConfig();
+	private TransportConfig sipTpConfig = new TransportConfig();
+	private String appDir;
+	
+	/* Maintain reference to log writer to avoid premature cleanup by GC */
+	private MyLogWriter logWriter;
+
+	private final String configName = "pjsua2.json";
+	private final int SIP_PORT  = 6000;
+	private final int LOG_LEVEL = 4;
+	
+	public void init(MyAppObserver obs, String app_dir) {
+		init(obs, app_dir, false);
+	}
+	
+	public void init(MyAppObserver obs, String app_dir, boolean own_worker_thread) {
+		observer = obs;
+		appDir = app_dir;
+		
+		/* Create endpoint */
+		try {
+			ep.libCreate();
+		} catch (Exception e) {
+			return;
+		}
+			
+		
+		/* Load config */
+		String configPath = appDir + "/" + configName;
+		File f = new File(configPath);
+		if (f.exists()) {
+			loadConfig(configPath);
+		} else {
+			/* Set 'default' values */
+			sipTpConfig.setPort(SIP_PORT);
+		}
+		
+		/* Override log level setting */
+		epConfig.getLogConfig().setLevel(LOG_LEVEL);
+		epConfig.getLogConfig().setConsoleLevel(LOG_LEVEL);
+		
+		/* Set log config. */
+		LogConfig log_cfg = epConfig.getLogConfig();
+		logWriter = new MyLogWriter();
+		log_cfg.setWriter(logWriter);
+		log_cfg.setDecor(log_cfg.getDecor() & 
+						 ~(pj_log_decoration.PJ_LOG_HAS_CR.swigValue() | 
+						   pj_log_decoration.PJ_LOG_HAS_NEWLINE.swigValue()));
+		
+		/* Set ua config. */
+		UaConfig ua_cfg = epConfig.getUaConfig();
+		ua_cfg.setUserAgent("Pjsua2And" + ep.libVersion().getFull());
+		if (own_worker_thread) {
+			ua_cfg.setThreadCnt(0);
+			ua_cfg.setMainThreadOnly(true);
+		}
+		
+		/* Init endpoint */
+		try {
+			ep.libInit(epConfig);
+		} catch (Exception e) {
+			return;
+		}
+		
+		/* Create transports. */
+		try {
+			ep.transportCreate(pjsip_transport_type_e.PJSIP_TRANSPORT_UDP, sipTpConfig);
+		} catch (Exception e) {
+			System.out.println(e);
+		}
+
+		try {
+			ep.transportCreate(pjsip_transport_type_e.PJSIP_TRANSPORT_TCP, sipTpConfig);
+		} catch (Exception e) {
+			System.out.println(e);
+		}
+		
+		/* Create accounts. */
+		for (int i = 0; i < accCfgs.size(); i++) {
+			MyAccountConfig my_cfg = accCfgs.get(i);
+			MyAccount acc = addAcc(my_cfg.accCfg);
+			if (acc == null)
+				continue;
+			
+			/* Add Buddies */
+			for (int j = 0; j < my_cfg.buddyCfgs.size(); j++) {
+				BuddyConfig bud_cfg = my_cfg.buddyCfgs.get(j);
+				acc.addBuddy(bud_cfg);
+			}
+		}
+
+		/* Start. */
+		try {
+			ep.libStart();
+		} catch (Exception e) {
+			return;
+		}
+	}
+	
+	public MyAccount addAcc(AccountConfig cfg) {
+		MyAccount acc = new MyAccount(cfg);
+		try {
+			acc.create(cfg);
+		} catch (Exception e) {
+			acc = null;
+			return null;
+		}
+		
+		accList.add(acc);
+		return acc;
+	}
+	
+	public void delAcc(MyAccount acc) {
+		accList.remove(acc);
+	}
+	
+	private void loadConfig(String filename) {
+		JsonDocument json = new JsonDocument();
+		
+		try {
+			/* Load file */
+			json.loadFile(filename);
+			ContainerNode root = json.getRootContainer();
+			
+			/* Read endpoint config */
+			epConfig.readObject(root);
+			
+			/* Read transport config */
+			ContainerNode tp_node = root.readContainer("SipTransport");
+			sipTpConfig.readObject(tp_node);
+			
+			/* Read account configs */
+			accCfgs.clear();
+			ContainerNode accs_node = root.readArray("accounts");
+			while (accs_node.hasUnread()) {
+				MyAccountConfig acc_cfg = new MyAccountConfig();
+				acc_cfg.readObject(accs_node);
+				accCfgs.add(acc_cfg);
+			}
+		} catch (Exception e) {
+			System.out.println(e);
+		}
+		
+		/* Force delete json now, as I found that Java somehow destroys it
+		 * after lib has been destroyed and from non-registered thread.
+		 */
+		json.delete();
+	}
+
+	private void buildAccConfigs() {
+		/* Sync accCfgs from accList */
+		accCfgs.clear();
+		for (int i = 0; i < accList.size(); i++) {
+			MyAccount acc = accList.get(i);
+			MyAccountConfig my_acc_cfg = new MyAccountConfig();
+			my_acc_cfg.accCfg = acc.cfg;
+			
+			my_acc_cfg.buddyCfgs.clear();
+			for (int j = 0; j < acc.buddyList.size(); j++) {
+				MyBuddy bud = acc.buddyList.get(j);
+				my_acc_cfg.buddyCfgs.add(bud.cfg);
+			}
+			
+			accCfgs.add(my_acc_cfg);
+		}
+	}
+	
+	private void saveConfig(String filename) {
+		JsonDocument json = new JsonDocument();
+		
+		try {
+			/* Write endpoint config */
+			json.writeObject(epConfig);
+			
+			/* Write transport config */
+			ContainerNode tp_node = json.writeNewContainer("SipTransport");
+			sipTpConfig.writeObject(tp_node);
+			
+			/* Write account configs */
+			buildAccConfigs();
+			ContainerNode accs_node = json.writeNewArray("accounts");
+			for (int i = 0; i < accCfgs.size(); i++) {
+				accCfgs.get(i).writeObject(accs_node);
+			}
+			
+			/* Save file */
+			json.saveFile(filename);
+		} catch (Exception e) {}
+
+		/* Force delete json now, as I found that Java somehow destroys it
+		 * after lib has been destroyed and from non-registered thread.
+		 */
+		json.delete();
+	}
+	
+	public void deinit() {
+		String configPath = appDir + "/" + configName;
+		saveConfig(configPath);
+		
+		/* Try force GC to avoid late destroy of PJ objects as they should be
+		 * deleted before lib is destroyed.
+		 */
+		Runtime.getRuntime().gc();
+		
+		/* Shutdown pjsua. Note that Endpoint destructor will also invoke
+		 * libDestroy(), so this will be a test of double libDestroy().
+		 */
+		try {
+			ep.libDestroy();
+		} catch (Exception e) {}
+		
+		/* Force delete Endpoint here, to avoid deletion from a non-
+		 * registered thread (by GC?). 
+		 */
+		ep.delete();
+		ep = null;
+	} 
+}
Index: /pjproject/trunk/pjsip-apps/src/swig/java/sample.java
===================================================================
--- /pjproject/trunk/pjsip-apps/src/swig/java/sample.java	(revision 4704)
+++ /pjproject/trunk/pjsip-apps/src/swig/java/sample.java	(revision 4704)
@@ -0,0 +1,140 @@
+/* $Id$ */
+/* 
+ * Copyright (C) 2013 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 
+ */
+ 
+package org.pjsip.pjsua2.app;
+
+import java.io.IOException;
+import org.pjsip.pjsua2.*;
+import org.pjsip.pjsua2.app.*;
+
+class MyObserver implements MyAppObserver {
+	private static MyCall currentCall = null;
+	
+	@Override
+	public void notifyRegState(pjsip_status_code code, String reason, int expiration) {}
+	
+	@Override
+	public void notifyIncomingCall(MyCall call) {
+		/* Auto answer. */
+		CallOpParam call_param = new CallOpParam();
+		call_param.setStatusCode(pjsip_status_code.PJSIP_SC_OK);
+		try {
+			currentCall = call;
+			currentCall.answer(call_param);
+		} catch (Exception e) {
+			System.out.println(e);
+			return;
+		}			
+	}
+	
+	@Override
+	public void notifyCallState(MyCall call) {
+		if (currentCall == null || call.getId() != currentCall.getId())
+			return;
+		
+		CallInfo ci;
+		try {
+			ci = call.getInfo();
+		} catch (Exception e) {
+			ci = null;
+		}
+		if (ci.getState() == pjsip_inv_state.PJSIP_INV_STATE_DISCONNECTED)
+			currentCall = null;		
+	}
+	
+	@Override
+	public void notifyBuddyState(MyBuddy buddy) {}	
+}
+
+class MyShutdownHook extends Thread {
+	Thread thread;
+	MyShutdownHook(Thread thr) {
+		thread = thr;
+	}
+	public void run() {
+		thread.interrupt();
+		try {
+			thread.join();
+		} catch (Exception e) {
+			;
+		}
+	}
+}	
+
+public class sample {
+	private static MyApp app = new MyApp();
+	private static MyAppObserver observer = new MyObserver();
+	private static MyAccount account = null;
+	private static AccountConfig accCfg = null;			
+	
+	private static void runWorker() {
+		try {					
+			app.init(observer, ".", true);
+		} catch (Exception e) {
+			System.out.println(e);
+			app.deinit();
+			System.exit(-1);
+		} 
+
+		if (app.accList.size() == 0) {
+			accCfg = new AccountConfig();
+			accCfg.setIdUri("sip:localhost");
+			account = app.addAcc(accCfg);
+
+			accCfg.setIdUri("sip:301@pjsip.org");
+			AccountSipConfig sipCfg = accCfg.getSipConfig();		
+			AuthCredInfoVector ciVec = sipCfg.getAuthCreds();
+			ciVec.add(new AuthCredInfo("Digest", 
+					"*",
+					"301",
+					0,
+					"pw301"));
+
+			StringVector proxy = sipCfg.getProxies();
+			proxy.add("sip:pjsip.org;transport=tcp");							
+
+			AccountRegConfig regCfg = accCfg.getRegConfig();
+			regCfg.setRegistrarUri("sip:pjsip.org");
+			account = app.addAcc(accCfg);
+		} else {
+			account = app.accList.get(0);
+			accCfg = account.cfg;
+		}				
+
+		try {
+			account.modify(accCfg);
+		} catch (Exception e) {}				
+
+		while (!Thread.currentThread().isInterrupted()) {
+			MyApp.ep.libHandleEvents(10);
+			try {						
+				Thread.currentThread().sleep(50);
+			} catch (InterruptedException ie) {						
+				break;
+			}					
+		}
+		app.deinit();
+	}	
+		
+	public static void main(String argv[]) {
+		Runtime.getRuntime().addShutdownHook(new MyShutdownHook(Thread.currentThread()));
+
+		runWorker();
+    }
+}
Index: /pjproject/trunk/pjsip-apps/src/swig/java/test.java
===================================================================
--- /pjproject/trunk/pjsip-apps/src/swig/java/test.java	(revision 4704)
+++ /pjproject/trunk/pjsip-apps/src/swig/java/test.java	(revision 4704)
@@ -0,0 +1,17 @@
+import org.pjsip.pjsua2.*;
+
+public class test {
+  static {
+    System.loadLibrary("pjsua2");
+    System.out.println("Library loaded");
+  }
+
+  public static void main(String argv[]) {
+
+	AuthCredInfo cred = new AuthCredInfo();
+
+	cred.setRealm("Hello world");
+    
+	System.out.println(cred.getRealm());
+  }
+}
Index: /pjproject/trunk/pjsip-apps/src/swig/pjsua2.i
===================================================================
--- /pjproject/trunk/pjsip-apps/src/swig/pjsua2.i	(revision 4704)
+++ /pjproject/trunk/pjsip-apps/src/swig/pjsua2.i	(revision 4704)
@@ -0,0 +1,111 @@
+%module(directors="1") pjsua2
+
+//
+// Suppress few warnings
+//
+#pragma SWIG nowarn=312		// 312: nested struct (in types.h, sip_auth.h)
+
+//
+// Header section
+//
+%{
+#include "pjsua2.hpp"
+using namespace std;
+using namespace pj;
+%}
+
+#ifdef SWIGPYTHON
+  %feature("director:except") {
+    if( $error != NULL ) {
+      PyObject *ptype, *pvalue, *ptraceback;
+      PyErr_Fetch( &ptype, &pvalue, &ptraceback );
+      PyErr_Restore( ptype, pvalue, ptraceback );
+      PyErr_Print();
+      //Py_Exit(1);
+    }
+  }
+#endif
+
+// Allow C++ exceptions to be handled in Java
+#ifdef SWIGJAVA
+  %typemap(throws, throws="java.lang.Exception") pj::Error {
+  jclass excep = jenv->FindClass("java/lang/Exception");
+  if (excep)
+    jenv->ThrowNew(excep, $1.info(true).c_str());
+  return $null;
+}
+
+  // Force the Error Java class to extend java.lang.Exception
+  %typemap(javabase) pj::Error "java.lang.Exception";
+
+  // Override getMessage()
+  %typemap(javacode) pj::Error %{
+  public String getMessage() {
+    return getTitle();
+  }
+%}
+#endif
+
+
+// Constants from PJSIP libraries
+%include "symbols.i"
+
+
+//
+// Classes that can be extended in the target language
+//
+%feature("director") LogWriter;
+%feature("director") Endpoint; 
+%feature("director") Account;
+%feature("director") Call;
+%feature("director") Buddy;
+%feature("director") FindBuddyMatch;
+
+//
+// STL stuff.
+//
+%include "std_string.i"
+%include "std_vector.i"
+
+%template(StringVector)			std::vector<std::string>;
+%template(IntVector) 			std::vector<int>;
+
+//
+// Ignore stuffs in pjsua2
+//
+%ignore fromPj;
+%ignore toPj;
+
+//
+// Now include the API itself.
+//
+%include "pjsua2/types.hpp"
+
+%ignore pj::ContainerNode::op;
+%ignore pj::ContainerNode::data;
+%ignore container_node_op;
+%ignore container_node_internal_data;
+%include "pjsua2/persistent.hpp"
+
+%include "pjsua2/siptypes.hpp"
+
+%template(SipHeaderVector)		std::vector<pj::SipHeader>;
+%template(AuthCredInfoVector)		std::vector<pj::AuthCredInfo>;
+%template(SipMultipartPartVector)	std::vector<pj::SipMultipartPart>;
+%template(BuddyVector)			std::vector<pj::Buddy*>;
+%template(AudioMediaVector)		std::vector<pj::AudioMedia*>;
+%template(MediaFormatVector)		std::vector<pj::MediaFormat*>;
+%template(AudioDevInfoVector)		std::vector<pj::AudioDevInfo*>;
+%template(CodecInfoVector)		std::vector<pj::CodecInfo*>;
+
+%include "pjsua2/media.hpp"
+%include "pjsua2/endpoint.hpp"
+%include "pjsua2/presence.hpp"
+%include "pjsua2/account.hpp"
+%include "pjsua2/call.hpp"
+
+%template(CallMediaInfoVector)          std::vector<pj::CallMediaInfo>;
+
+%ignore pj::JsonDocument::allocElement;
+%ignore pj::JsonDocument::getPool;
+%include "pjsua2/json.hpp"
Index: /pjproject/trunk/pjsip-apps/src/swig/python/Makefile
===================================================================
--- /pjproject/trunk/pjsip-apps/src/swig/python/Makefile	(revision 4704)
+++ /pjproject/trunk/pjsip-apps/src/swig/python/Makefile	(revision 4704)
@@ -0,0 +1,29 @@
+PYTHON_SO=_pjsua2.so
+
+#PYTHON_SETUP_FLAGS = --inplace 
+ifeq ($(OS),Windows_NT)
+  PYTHON_SETUP_FLAGS += --compiler=mingw32
+endif
+
+SWIG_FLAGS += -w312
+
+.PHONY: all install uninstall
+
+all: $(PYTHON_SO)
+
+$(PYTHON_SO): pjsua2_wrap.cpp setup.py
+	python setup.py build $(PYTHON_SETUP_FLAGS)
+
+pjsua2_wrap.cpp: ../pjsua2.i ../symbols.i Makefile $(SRCS)
+	swig $(SWIG_FLAGS) -python  -o pjsua2_wrap.cpp ../pjsua2.i
+
+clean distclean realclean:
+	rm -rf $(PYTHON_SO) pjsua2_wrap.cpp pjsua2_wrap.h pjsua2.py build *.pyc
+
+install:
+	python 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*
+
Index: /pjproject/trunk/pjsip-apps/src/swig/python/helper.mak
===================================================================
--- /pjproject/trunk/pjsip-apps/src/swig/python/helper.mak	(revision 4704)
+++ /pjproject/trunk/pjsip-apps/src/swig/python/helper.mak	(revision 4704)
@@ -0,0 +1,20 @@
+include ../../../../build.mak
+
+lib_dir:
+	@for token in `echo $(APP_LDFLAGS)`; do \
+		echo $$token | grep L | sed 's/-L//'; \
+	done
+
+inc_dir:
+	@for token in `echo $(APP_CFLAGS)`; do \
+		echo $$token | grep I | sed 's/-I//'; \
+	done
+
+libs:
+	@for token in `echo $(APP_LDLIBS)`; do \
+		echo $$token | grep \\-l | sed 's/-l//'; \
+	done
+
+target_name:
+	@echo $(TARGET_NAME)
+	
Index: /pjproject/trunk/pjsip-apps/src/swig/python/setup.py
===================================================================
--- /pjproject/trunk/pjsip-apps/src/swig/python/setup.py	(revision 4704)
+++ /pjproject/trunk/pjsip-apps/src/swig/python/setup.py	(revision 4704)
@@ -0,0 +1,118 @@
+# $Id$
+#
+# pjsua2 Setup script.
+#
+# Copyright (C)2012 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 
+#
+from distutils.core import setup, Extension
+import os
+import sys
+import platform
+
+# find pjsip version
+pj_version=""
+pj_version_major=""
+pj_version_minor=""
+pj_version_rev=""
+pj_version_suffix=""
+f = open('../../../../version.mak', 'r')
+for line in f:
+    if line.find("export PJ_VERSION_MAJOR") != -1:
+    	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()
+    elif line.find("export PJ_VERSION_REV") != -1:
+    	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()
+
+f.close()
+if not pj_version_major:
+    print 'Unable to get PJ_VERSION_MAJOR'
+    sys.exit(1)
+
+pj_version = pj_version_major + "." + pj_version_minor
+if pj_version_rev:
+	pj_version += "." + pj_version_rev
+if pj_version_suffix:
+	pj_version += "-" + pj_version_suffix
+
+#print 'PJ_VERSION = "'+ pj_version + '"'
+
+# Get targetname
+f = os.popen("make --no-print-directory -f helper.mak target_name")
+pj_target_name = f.read().rstrip("\r\n")
+f.close()
+
+# Fill in pj_inc_dirs
+pj_inc_dirs = []
+f = os.popen("make --no-print-directory -f helper.mak inc_dir")
+for line in f:
+    pj_inc_dirs.append(line.rstrip("\r\n"))
+f.close()
+
+# Fill in pj_lib_dirs
+pj_lib_dirs = []
+f = os.popen("make --no-print-directory -f helper.mak lib_dir")
+for line in f:
+    pj_lib_dirs.append(line.rstrip("\r\n"))
+f.close()
+
+# Fill in pj_libs
+pj_libs = ['pjsua2-' + pj_target_name]
+f = os.popen("make --no-print-directory -f helper.mak libs")
+for line in f:
+    pj_libs.append(line.rstrip("\r\n"))
+f.close()
+
+# Fill in extra link args
+extra_link_args = ['-static-libstdc++']
+if platform.system() == 'Darwin':
+    # Mac OS X depedencies
+    extra_link_args += ["-framework", "CoreFoundation", 
+                        "-framework", "AudioToolbox",
+			"-framework", "QTKit"]
+    # OS X Lion support
+    if platform.mac_ver()[0].startswith("10.7"):
+        extra_link_args += ["-framework", "AudioUnit"]
+
+
+setup(name="pjsua2", 
+      version=pj_version,
+      description='SIP User Agent Library based on PJSIP',
+      url='http://www.pjsip.org',
+      ext_modules = [Extension("_pjsua2", 
+                               ["pjsua2_wrap.cpp"], 
+                               define_macros=[('PJ_AUTOCONF', '1'),],
+                               include_dirs=pj_inc_dirs, 
+                               library_dirs=pj_lib_dirs, 
+                               libraries=pj_libs,
+                               extra_link_args=extra_link_args
+                              )
+                    ],
+      py_modules=["pjsua2"]
+     )
+
+
Index: /pjproject/trunk/pjsip-apps/src/swig/python/test.py
===================================================================
--- /pjproject/trunk/pjsip-apps/src/swig/python/test.py	(revision 4704)
+++ /pjproject/trunk/pjsip-apps/src/swig/python/test.py	(revision 4704)
@@ -0,0 +1,112 @@
+import pjsua2 as pj
+import sys
+
+#
+# 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 ""
+
+#
+# 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
+
+#
+# Custom log writer
+#
+class MyLogWriter(pj.LogWriter):
+	def write(self, entry):
+		print "This is Python:", entry.msg
+		
+#
+# 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()
+	
+#
+# 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()
+
+#
+# main()
+#
+if __name__ == "__main__":
+	ua_data_test()
+	ua_run_test_exception()
+	ua_run_log_test()
+	ua_run_ua_test()
+	sys.exit(0)
+
+	
Index: /pjproject/trunk/pjsip-apps/src/swig/symbols.i
===================================================================
--- /pjproject/trunk/pjsip-apps/src/swig/symbols.i	(revision 4704)
+++ /pjproject/trunk/pjsip-apps/src/swig/symbols.i	(revision 4704)
@@ -0,0 +1,130 @@
+// This file is autogenerated by importsym script, do not modify!
+
+typedef int pj_status_t;
+
+enum pj_constants_ {PJ_SUCCESS = 0, PJ_TRUE = 1, PJ_FALSE = 0};
+
+typedef unsigned char pj_uint8_t;
+
+typedef int pj_int32_t;
+
+typedef unsigned int pj_uint32_t;
+
+typedef unsigned short pj_uint16_t;
+
+enum pj_file_access {PJ_O_RDONLY = 0x1101, PJ_O_WRONLY = 0x1102, PJ_O_RDWR = 0x1103, PJ_O_APPEND = 0x1108};
+
+enum pj_log_decoration {PJ_LOG_HAS_DAY_NAME = 1, PJ_LOG_HAS_YEAR = 2, PJ_LOG_HAS_MONTH = 4, PJ_LOG_HAS_DAY_OF_MON = 8, PJ_LOG_HAS_TIME = 16, PJ_LOG_HAS_MICRO_SEC = 32, PJ_LOG_HAS_SENDER = 64, PJ_LOG_HAS_NEWLINE = 128, PJ_LOG_HAS_CR = 256, PJ_LOG_HAS_SPACE = 512, PJ_LOG_HAS_COLOR = 1024, PJ_LOG_HAS_LEVEL_TEXT = 2048, PJ_LOG_HAS_THREAD_ID = 4096, PJ_LOG_HAS_THREAD_SWC = 8192, PJ_LOG_HAS_INDENT = 16384};
+
+typedef enum pj_qos_type {PJ_QOS_TYPE_BEST_EFFORT, PJ_QOS_TYPE_BACKGROUND, PJ_QOS_TYPE_VIDEO, PJ_QOS_TYPE_VOICE, PJ_QOS_TYPE_CONTROL} pj_qos_type;
+
+typedef enum pj_qos_flag {PJ_QOS_PARAM_HAS_DSCP = 1, PJ_QOS_PARAM_HAS_SO_PRIO = 2, PJ_QOS_PARAM_HAS_WMM = 4} pj_qos_flag;
+
+typedef enum pj_qos_wmm_prio {PJ_QOS_WMM_PRIO_BULK_EFFORT, PJ_QOS_WMM_PRIO_BULK, PJ_QOS_WMM_PRIO_VIDEO, PJ_QOS_WMM_PRIO_VOICE} pj_qos_wmm_prio;
+
+typedef struct pj_qos_params
+{
+  pj_uint8_t flags;
+  pj_uint8_t dscp_val;
+  pj_uint8_t so_prio;
+  pj_qos_wmm_prio wmm_prio;
+} pj_qos_params;
+
+typedef enum pj_ssl_cipher {PJ_TLS_NULL_WITH_NULL_NULL = 0x00000000, PJ_TLS_RSA_WITH_NULL_MD5 = 0x00000001, PJ_TLS_RSA_WITH_NULL_SHA = 0x00000002, PJ_TLS_RSA_WITH_NULL_SHA256 = 0x0000003B, PJ_TLS_RSA_WITH_RC4_128_MD5 = 0x00000004, PJ_TLS_RSA_WITH_RC4_128_SHA = 0x00000005, PJ_TLS_RSA_WITH_3DES_EDE_CBC_SHA = 0x0000000A, PJ_TLS_RSA_WITH_AES_128_CBC_SHA = 0x0000002F, PJ_TLS_RSA_WITH_AES_256_CBC_SHA = 0x00000035, PJ_TLS_RSA_WITH_AES_128_CBC_SHA256 = 0x0000003C, PJ_TLS_RSA_WITH_AES_256_CBC_SHA256 = 0x0000003D, PJ_TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA = 0x0000000D, PJ_TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA = 0x00000010, PJ_TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA = 0x00000013, PJ_TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA = 0x00000016, PJ_TLS_DH_DSS_WITH_AES_128_CBC_SHA = 0x00000030, PJ_TLS_DH_RSA_WITH_AES_128_CBC_SHA = 0x00000031, PJ_TLS_DHE_DSS_WITH_AES_128_CBC_SHA = 0x00000032, PJ_TLS_DHE_RSA_WITH_AES_128_CBC_SHA = 0x00000033, PJ_TLS_DH_DSS_WITH_AES_256_CBC_SHA = 0x00000036, PJ_TLS_DH_RSA_WITH_AES_256_CBC_SHA = 0x00000037, PJ_TLS_DHE_DSS_WITH_AES_256_CBC_SHA = 0x00000038, PJ_TLS_DHE_RSA_WITH_AES_256_CBC_SHA = 0x00000039, PJ_TLS_DH_DSS_WITH_AES_128_CBC_SHA256 = 0x0000003E, PJ_TLS_DH_RSA_WITH_AES_128_CBC_SHA256 = 0x0000003F, PJ_TLS_DHE_DSS_WITH_AES_128_CBC_SHA256 = 0x00000040, PJ_TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 = 0x00000067, PJ_TLS_DH_DSS_WITH_AES_256_CBC_SHA256 = 0x00000068, PJ_TLS_DH_RSA_WITH_AES_256_CBC_SHA256 = 0x00000069, PJ_TLS_DHE_DSS_WITH_AES_256_CBC_SHA256 = 0x0000006A, PJ_TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 = 0x0000006B, PJ_TLS_DH_anon_WITH_RC4_128_MD5 = 0x00000018, PJ_TLS_DH_anon_WITH_3DES_EDE_CBC_SHA = 0x0000001B, PJ_TLS_DH_anon_WITH_AES_128_CBC_SHA = 0x00000034, PJ_TLS_DH_anon_WITH_AES_256_CBC_SHA = 0x0000003A, PJ_TLS_DH_anon_WITH_AES_128_CBC_SHA256 = 0x0000006C, PJ_TLS_DH_anon_WITH_AES_256_CBC_SHA256 = 0x0000006D, PJ_TLS_RSA_EXPORT_WITH_RC4_40_MD5 = 0x00000003, PJ_TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5 = 0x00000006, PJ_TLS_RSA_WITH_IDEA_CBC_SHA = 0x00000007, PJ_TLS_RSA_EXPORT_WITH_DES40_CBC_SHA = 0x00000008, PJ_TLS_RSA_WITH_DES_CBC_SHA = 0x00000009, PJ_TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA = 0x0000000B, PJ_TLS_DH_DSS_WITH_DES_CBC_SHA = 0x0000000C, PJ_TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA = 0x0000000E, PJ_TLS_DH_RSA_WITH_DES_CBC_SHA = 0x0000000F, PJ_TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA = 0x00000011, PJ_TLS_DHE_DSS_WITH_DES_CBC_SHA = 0x00000012, PJ_TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA = 0x00000014, PJ_TLS_DHE_RSA_WITH_DES_CBC_SHA = 0x00000015, PJ_TLS_DH_anon_EXPORT_WITH_RC4_40_MD5 = 0x00000017, PJ_TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA = 0x00000019, PJ_TLS_DH_anon_WITH_DES_CBC_SHA = 0x0000001A, PJ_SSL_FORTEZZA_KEA_WITH_NULL_SHA = 0x0000001C, PJ_SSL_FORTEZZA_KEA_WITH_FORTEZZA_CBC_SHA = 0x0000001D, PJ_SSL_FORTEZZA_KEA_WITH_RC4_128_SHA = 0x0000001E, PJ_SSL_CK_RC4_128_WITH_MD5 = 0x00010080, PJ_SSL_CK_RC4_128_EXPORT40_WITH_MD5 = 0x00020080, PJ_SSL_CK_RC2_128_CBC_WITH_MD5 = 0x00030080, PJ_SSL_CK_RC2_128_CBC_EXPORT40_WITH_MD5 = 0x00040080, PJ_SSL_CK_IDEA_128_CBC_WITH_MD5 = 0x00050080, PJ_SSL_CK_DES_64_CBC_WITH_MD5 = 0x00060040, PJ_SSL_CK_DES_192_EDE3_CBC_WITH_MD5 = 0x000700C0} pj_ssl_cipher;
+
+typedef enum pj_stun_nat_type {PJ_STUN_NAT_TYPE_UNKNOWN, PJ_STUN_NAT_TYPE_ERR_UNKNOWN, PJ_STUN_NAT_TYPE_OPEN, PJ_STUN_NAT_TYPE_BLOCKED, PJ_STUN_NAT_TYPE_SYMMETRIC_UDP, PJ_STUN_NAT_TYPE_FULL_CONE, PJ_STUN_NAT_TYPE_SYMMETRIC, PJ_STUN_NAT_TYPE_RESTRICTED, PJ_STUN_NAT_TYPE_PORT_RESTRICTED} pj_stun_nat_type;
+
+typedef enum pj_turn_tp_type {PJ_TURN_TP_UDP = 17, PJ_TURN_TP_TCP = 6, PJ_TURN_TP_TLS = 255} pj_turn_tp_type;
+
+typedef enum pjmedia_event_type {PJMEDIA_EVENT_NONE, PJMEDIA_EVENT_FMT_CHANGED = ((('H' << 24) | ('C' << 16)) | ('M' << 8)) | 'F', PJMEDIA_EVENT_WND_CLOSING = ((('L' << 24) | ('C' << 16)) | ('N' << 8)) | 'W', PJMEDIA_EVENT_WND_CLOSED = ((('O' << 24) | ('C' << 16)) | ('N' << 8)) | 'W', PJMEDIA_EVENT_WND_RESIZED = ((('Z' << 24) | ('R' << 16)) | ('N' << 8)) | 'W', PJMEDIA_EVENT_MOUSE_BTN_DOWN = ((('N' << 24) | ('D' << 16)) | ('S' << 8)) | 'M', PJMEDIA_EVENT_KEYFRAME_FOUND = ((('F' << 24) | ('R' << 16)) | ('F' << 8)) | 'I', PJMEDIA_EVENT_KEYFRAME_MISSING = ((('M' << 24) | ('R' << 16)) | ('F' << 8)) | 'I', PJMEDIA_EVENT_ORIENT_CHANGED = ((('T' << 24) | ('N' << 16)) | ('R' << 8)) | 'O'} pjmedia_event_type;
+
+typedef enum pjmedia_srtp_use {PJMEDIA_SRTP_DISABLED, PJMEDIA_SRTP_OPTIONAL, PJMEDIA_SRTP_MANDATORY} pjmedia_srtp_use;
+
+typedef enum pjmedia_vid_stream_rc_method {PJMEDIA_VID_STREAM_RC_NONE = 0, PJMEDIA_VID_STREAM_RC_SIMPLE_BLOCKING = 1} pjmedia_vid_stream_rc_method;
+
+typedef pj_int32_t pjmedia_vid_dev_index;
+
+enum pjmedia_vid_dev_std_index {PJMEDIA_VID_DEFAULT_CAPTURE_DEV = -1, PJMEDIA_VID_DEFAULT_RENDER_DEV = -2, PJMEDIA_VID_INVALID_DEV = -3};
+
+typedef enum pjmedia_aud_dev_route {PJMEDIA_AUD_DEV_ROUTE_DEFAULT = 0, PJMEDIA_AUD_DEV_ROUTE_LOUDSPEAKER = 1, PJMEDIA_AUD_DEV_ROUTE_EARPIECE = 2, PJMEDIA_AUD_DEV_ROUTE_BLUETOOTH = 4} pjmedia_aud_dev_route;
+
+typedef enum pjmedia_aud_dev_cap {PJMEDIA_AUD_DEV_CAP_EXT_FORMAT = 1, PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY = 2, PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY = 4, PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING = 8, PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING = 16, PJMEDIA_AUD_DEV_CAP_INPUT_SIGNAL_METER = 32, PJMEDIA_AUD_DEV_CAP_OUTPUT_SIGNAL_METER = 64, PJMEDIA_AUD_DEV_CAP_INPUT_ROUTE = 128, PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE = 256, PJMEDIA_AUD_DEV_CAP_EC = 512, PJMEDIA_AUD_DEV_CAP_EC_TAIL = 1024, PJMEDIA_AUD_DEV_CAP_VAD = 2048, PJMEDIA_AUD_DEV_CAP_CNG = 4096, PJMEDIA_AUD_DEV_CAP_PLC = 8192, PJMEDIA_AUD_DEV_CAP_MAX = 16384} pjmedia_aud_dev_cap;
+
+enum pjmedia_file_writer_option {PJMEDIA_FILE_WRITE_PCM = 0, PJMEDIA_FILE_WRITE_ALAW = 1, PJMEDIA_FILE_WRITE_ULAW = 2};
+
+enum pjmedia_file_player_option {PJMEDIA_FILE_NO_LOOP = 1};
+
+typedef enum pjmedia_type {PJMEDIA_TYPE_NONE, PJMEDIA_TYPE_AUDIO, PJMEDIA_TYPE_VIDEO, PJMEDIA_TYPE_APPLICATION, PJMEDIA_TYPE_UNKNOWN} pjmedia_type;
+
+typedef enum pjmedia_dir {PJMEDIA_DIR_NONE = 0, PJMEDIA_DIR_ENCODING = 1, PJMEDIA_DIR_CAPTURE = PJMEDIA_DIR_ENCODING, PJMEDIA_DIR_DECODING = 2, PJMEDIA_DIR_PLAYBACK = PJMEDIA_DIR_DECODING, PJMEDIA_DIR_RENDER = PJMEDIA_DIR_DECODING, PJMEDIA_DIR_ENCODING_DECODING = 3, PJMEDIA_DIR_CAPTURE_PLAYBACK = PJMEDIA_DIR_ENCODING_DECODING, PJMEDIA_DIR_CAPTURE_RENDER = PJMEDIA_DIR_ENCODING_DECODING} pjmedia_dir;
+
+typedef enum pjmedia_tp_proto {PJMEDIA_TP_PROTO_NONE = 0, PJMEDIA_TP_PROTO_RTP_AVP, PJMEDIA_TP_PROTO_RTP_SAVP, PJMEDIA_TP_PROTO_UNKNOWN} pjmedia_tp_proto;
+
+typedef enum pjmedia_format_id {PJMEDIA_FORMAT_L16 = 0, PJMEDIA_FORMAT_PCM = PJMEDIA_FORMAT_L16, PJMEDIA_FORMAT_PCMA = ((('W' << 24) | ('A' << 16)) | ('L' << 8)) | 'A', PJMEDIA_FORMAT_ALAW = PJMEDIA_FORMAT_PCMA, PJMEDIA_FORMAT_PCMU = ((('W' << 24) | ('A' << 16)) | ('L' << 8)) | 'u', PJMEDIA_FORMAT_ULAW = PJMEDIA_FORMAT_PCMU, PJMEDIA_FORMAT_AMR = ((('R' << 24) | ('M' << 16)) | ('A' << 8)) | ' ', PJMEDIA_FORMAT_G729 = ((('9' << 24) | ('2' << 16)) | ('7' << 8)) | 'G', PJMEDIA_FORMAT_ILBC = ((('C' << 24) | ('B' << 16)) | ('L' << 8)) | 'I', PJMEDIA_FORMAT_RGB24 = ((('3' << 24) | ('B' << 16)) | ('G' << 8)) | 'R', PJMEDIA_FORMAT_RGBA = ((('A' << 24) | ('B' << 16)) | ('G' << 8)) | 'R', PJMEDIA_FORMAT_BGRA = ((('A' << 24) | ('R' << 16)) | ('G' << 8)) | 'B', PJMEDIA_FORMAT_RGB32 = PJMEDIA_FORMAT_RGBA, PJMEDIA_FORMAT_DIB = (((' ' << 24) | ('B' << 16)) | ('I' << 8)) | 'D', PJMEDIA_FORMAT_GBRP = ((('P' << 24) | ('R' << 16)) | ('B' << 8)) | 'G', PJMEDIA_FORMAT_AYUV = ((('V' << 24) | ('U' << 16)) | ('Y' << 8)) | 'A', PJMEDIA_FORMAT_YUY2 = ((('2' << 24) | ('Y' << 16)) | ('U' << 8)) | 'Y', PJMEDIA_FORMAT_UYVY = ((('Y' << 24) | ('V' << 16)) | ('Y' << 8)) | 'U', PJMEDIA_FORMAT_YVYU = ((('U' << 24) | ('Y' << 16)) | ('V' << 8)) | 'Y', PJMEDIA_FORMAT_I420 = ((('0' << 24) | ('2' << 16)) | ('4' << 8)) | 'I', PJMEDIA_FORMAT_IYUV = PJMEDIA_FORMAT_I420, PJMEDIA_FORMAT_YV12 = ((('2' << 24) | ('1' << 16)) | ('V' << 8)) | 'Y', PJMEDIA_FORMAT_I422 = ((('2' << 24) | ('2' << 16)) | ('4' << 8)) | 'I', PJMEDIA_FORMAT_I420JPEG = ((('0' << 24) | ('2' << 16)) | ('4' << 8)) | 'J', PJMEDIA_FORMAT_I422JPEG = ((('2' << 24) | ('2' << 16)) | ('4' << 8)) | 'J', PJMEDIA_FORMAT_H261 = ((('1' << 24) | ('6' << 16)) | ('2' << 8)) | 'H', PJMEDIA_FORMAT_H263 = ((('3' << 24) | ('6' << 16)) | ('2' << 8)) | 'H', PJMEDIA_FORMAT_H263P = ((('3' << 24) | ('6' << 16)) | ('2' << 8)) | 'P', PJMEDIA_FORMAT_H264 = ((('4' << 24) | ('6' << 16)) | ('2' << 8)) | 'H', PJMEDIA_FORMAT_MJPEG = ((('G' << 24) | ('P' << 16)) | ('J' << 8)) | 'M', PJMEDIA_FORMAT_MPEG1VIDEO = ((('V' << 24) | ('1' << 16)) | ('P' << 8)) | 'M', PJMEDIA_FORMAT_MPEG2VIDEO = ((('V' << 24) | ('2' << 16)) | ('P' << 8)) | 'M', PJMEDIA_FORMAT_MPEG4 = ((('4' << 24) | ('G' << 16)) | ('P' << 8)) | 'M'} pjmedia_format_id;
+
+typedef enum pjsip_cred_data_type {PJSIP_CRED_DATA_PLAIN_PASSWD = 0, PJSIP_CRED_DATA_DIGEST = 1, PJSIP_CRED_DATA_EXT_AKA = 16} pjsip_cred_data_type;
+
+typedef enum pjsip_dialog_cap_status {PJSIP_DIALOG_CAP_UNSUPPORTED = 0, PJSIP_DIALOG_CAP_SUPPORTED = 1, PJSIP_DIALOG_CAP_UNKNOWN = 2} pjsip_dialog_cap_status;
+
+typedef enum pjsip_event_id_e {PJSIP_EVENT_UNKNOWN, PJSIP_EVENT_TIMER, PJSIP_EVENT_TX_MSG, PJSIP_EVENT_RX_MSG, PJSIP_EVENT_TRANSPORT_ERROR, PJSIP_EVENT_TSX_STATE, PJSIP_EVENT_USER} pjsip_event_id_e;
+
+typedef enum pjsip_status_code {PJSIP_SC_TRYING = 100, PJSIP_SC_RINGING = 180, PJSIP_SC_CALL_BEING_FORWARDED = 181, PJSIP_SC_QUEUED = 182, PJSIP_SC_PROGRESS = 183, PJSIP_SC_OK = 200, PJSIP_SC_ACCEPTED = 202, PJSIP_SC_MULTIPLE_CHOICES = 300, PJSIP_SC_MOVED_PERMANENTLY = 301, PJSIP_SC_MOVED_TEMPORARILY = 302, PJSIP_SC_USE_PROXY = 305, PJSIP_SC_ALTERNATIVE_SERVICE = 380, PJSIP_SC_BAD_REQUEST = 400, PJSIP_SC_UNAUTHORIZED = 401, PJSIP_SC_PAYMENT_REQUIRED = 402, PJSIP_SC_FORBIDDEN = 403, PJSIP_SC_NOT_FOUND = 404, PJSIP_SC_METHOD_NOT_ALLOWED = 405, PJSIP_SC_NOT_ACCEPTABLE = 406, PJSIP_SC_PROXY_AUTHENTICATION_REQUIRED = 407, PJSIP_SC_REQUEST_TIMEOUT = 408, PJSIP_SC_GONE = 410, PJSIP_SC_REQUEST_ENTITY_TOO_LARGE = 413, PJSIP_SC_REQUEST_URI_TOO_LONG = 414, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE = 415, PJSIP_SC_UNSUPPORTED_URI_SCHEME = 416, PJSIP_SC_BAD_EXTENSION = 420, PJSIP_SC_EXTENSION_REQUIRED = 421, PJSIP_SC_SESSION_TIMER_TOO_SMALL = 422, PJSIP_SC_INTERVAL_TOO_BRIEF = 423, PJSIP_SC_TEMPORARILY_UNAVAILABLE = 480, PJSIP_SC_CALL_TSX_DOES_NOT_EXIST = 481, PJSIP_SC_LOOP_DETECTED = 482, PJSIP_SC_TOO_MANY_HOPS = 483, PJSIP_SC_ADDRESS_INCOMPLETE = 484, PJSIP_AC_AMBIGUOUS = 485, PJSIP_SC_BUSY_HERE = 486, PJSIP_SC_REQUEST_TERMINATED = 487, PJSIP_SC_NOT_ACCEPTABLE_HERE = 488, PJSIP_SC_BAD_EVENT = 489, PJSIP_SC_REQUEST_UPDATED = 490, PJSIP_SC_REQUEST_PENDING = 491, PJSIP_SC_UNDECIPHERABLE = 493, PJSIP_SC_INTERNAL_SERVER_ERROR = 500, PJSIP_SC_NOT_IMPLEMENTED = 501, PJSIP_SC_BAD_GATEWAY = 502, PJSIP_SC_SERVICE_UNAVAILABLE = 503, PJSIP_SC_SERVER_TIMEOUT = 504, PJSIP_SC_VERSION_NOT_SUPPORTED = 505, PJSIP_SC_MESSAGE_TOO_LARGE = 513, PJSIP_SC_PRECONDITION_FAILURE = 580, PJSIP_SC_BUSY_EVERYWHERE = 600, PJSIP_SC_DECLINE = 603, PJSIP_SC_DOES_NOT_EXIST_ANYWHERE = 604, PJSIP_SC_NOT_ACCEPTABLE_ANYWHERE = 606, PJSIP_SC_TSX_TIMEOUT = PJSIP_SC_REQUEST_TIMEOUT, PJSIP_SC_TSX_TRANSPORT_ERROR = PJSIP_SC_SERVICE_UNAVAILABLE, PJSIP_SC__force_32bit = 0x7FFFFFFF} pjsip_status_code;
+
+typedef enum pjsip_hdr_e {PJSIP_H_ACCEPT, PJSIP_H_ACCEPT_ENCODING_UNIMP, PJSIP_H_ACCEPT_LANGUAGE_UNIMP, PJSIP_H_ALERT_INFO_UNIMP, PJSIP_H_ALLOW, PJSIP_H_AUTHENTICATION_INFO_UNIMP, PJSIP_H_AUTHORIZATION, PJSIP_H_CALL_ID, PJSIP_H_CALL_INFO_UNIMP, PJSIP_H_CONTACT, PJSIP_H_CONTENT_DISPOSITION_UNIMP, PJSIP_H_CONTENT_ENCODING_UNIMP, PJSIP_H_CONTENT_LANGUAGE_UNIMP, PJSIP_H_CONTENT_LENGTH, PJSIP_H_CONTENT_TYPE, PJSIP_H_CSEQ, PJSIP_H_DATE_UNIMP, PJSIP_H_ERROR_INFO_UNIMP, PJSIP_H_EXPIRES, PJSIP_H_FROM, PJSIP_H_IN_REPLY_TO_UNIMP, PJSIP_H_MAX_FORWARDS, PJSIP_H_MIME_VERSION_UNIMP, PJSIP_H_MIN_EXPIRES, PJSIP_H_ORGANIZATION_UNIMP, PJSIP_H_PRIORITY_UNIMP, PJSIP_H_PROXY_AUTHENTICATE, PJSIP_H_PROXY_AUTHORIZATION, PJSIP_H_PROXY_REQUIRE_UNIMP, PJSIP_H_RECORD_ROUTE, PJSIP_H_REPLY_TO_UNIMP, PJSIP_H_REQUIRE, PJSIP_H_RETRY_AFTER, PJSIP_H_ROUTE, PJSIP_H_SERVER_UNIMP, PJSIP_H_SUBJECT_UNIMP, PJSIP_H_SUPPORTED, PJSIP_H_TIMESTAMP_UNIMP, PJSIP_H_TO, PJSIP_H_UNSUPPORTED, PJSIP_H_USER_AGENT_UNIMP, PJSIP_H_VIA, PJSIP_H_WARNING_UNIMP, PJSIP_H_WWW_AUTHENTICATE, PJSIP_H_OTHER} pjsip_hdr_e;
+
+typedef enum pjsip_transport_type_e {PJSIP_TRANSPORT_UNSPECIFIED, PJSIP_TRANSPORT_UDP, PJSIP_TRANSPORT_TCP, PJSIP_TRANSPORT_TLS, PJSIP_TRANSPORT_SCTP, PJSIP_TRANSPORT_LOOP, PJSIP_TRANSPORT_LOOP_DGRAM, PJSIP_TRANSPORT_START_OTHER, PJSIP_TRANSPORT_IPV6 = 128, PJSIP_TRANSPORT_UDP6 = PJSIP_TRANSPORT_UDP + PJSIP_TRANSPORT_IPV6, PJSIP_TRANSPORT_TCP6 = PJSIP_TRANSPORT_TCP + PJSIP_TRANSPORT_IPV6, PJSIP_TRANSPORT_TLS6 = PJSIP_TRANSPORT_TLS + PJSIP_TRANSPORT_IPV6} pjsip_transport_type_e;
+
+enum pjsip_transport_flags_e {PJSIP_TRANSPORT_RELIABLE = 1, PJSIP_TRANSPORT_SECURE = 2, PJSIP_TRANSPORT_DATAGRAM = 4};
+
+typedef enum pjsip_transport_state {PJSIP_TP_STATE_CONNECTED, PJSIP_TP_STATE_DISCONNECTED} pjsip_transport_state;
+
+typedef enum pjsip_ssl_method {PJSIP_SSL_UNSPECIFIED_METHOD = 0, PJSIP_TLSV1_METHOD = 31, PJSIP_SSLV2_METHOD = 20, PJSIP_SSLV3_METHOD = 30, PJSIP_SSLV23_METHOD = 23} pjsip_ssl_method;
+
+typedef enum pjsip_tsx_state_e {PJSIP_TSX_STATE_NULL, PJSIP_TSX_STATE_CALLING, PJSIP_TSX_STATE_TRYING, PJSIP_TSX_STATE_PROCEEDING, PJSIP_TSX_STATE_COMPLETED, PJSIP_TSX_STATE_CONFIRMED, PJSIP_TSX_STATE_TERMINATED, PJSIP_TSX_STATE_DESTROYED, PJSIP_TSX_STATE_MAX} pjsip_tsx_state_e;
+
+typedef enum pjsip_role_e {PJSIP_ROLE_UAC, PJSIP_ROLE_UAS, PJSIP_UAC_ROLE = PJSIP_ROLE_UAC, PJSIP_UAS_ROLE = PJSIP_ROLE_UAS} pjsip_role_e;
+
+typedef enum pjsip_redirect_op {PJSIP_REDIRECT_REJECT, PJSIP_REDIRECT_ACCEPT, PJSIP_REDIRECT_ACCEPT_REPLACE, PJSIP_REDIRECT_PENDING, PJSIP_REDIRECT_STOP} pjsip_redirect_op;
+
+typedef enum pjrpid_activity {PJRPID_ACTIVITY_UNKNOWN, PJRPID_ACTIVITY_AWAY, PJRPID_ACTIVITY_BUSY} pjrpid_activity;
+
+typedef enum pjsip_evsub_state {PJSIP_EVSUB_STATE_NULL, PJSIP_EVSUB_STATE_SENT, PJSIP_EVSUB_STATE_ACCEPTED, PJSIP_EVSUB_STATE_PENDING, PJSIP_EVSUB_STATE_ACTIVE, PJSIP_EVSUB_STATE_TERMINATED, PJSIP_EVSUB_STATE_UNKNOWN} pjsip_evsub_state;
+
+typedef enum pjsip_inv_state {PJSIP_INV_STATE_NULL, PJSIP_INV_STATE_CALLING, PJSIP_INV_STATE_INCOMING, PJSIP_INV_STATE_EARLY, PJSIP_INV_STATE_CONNECTING, PJSIP_INV_STATE_CONFIRMED, PJSIP_INV_STATE_DISCONNECTED} pjsip_inv_state;
+
+enum pjsua_invalid_id_const_ {PJSUA_INVALID_ID = -1};
+
+typedef enum pjsua_state {PJSUA_STATE_NULL, PJSUA_STATE_CREATED, PJSUA_STATE_INIT, PJSUA_STATE_STARTING, PJSUA_STATE_RUNNING, PJSUA_STATE_CLOSING} pjsua_state;
+
+typedef enum pjsua_stun_use {PJSUA_STUN_USE_DEFAULT, PJSUA_STUN_USE_DISABLED} pjsua_stun_use;
+
+typedef enum pjsua_call_hold_type {PJSUA_CALL_HOLD_TYPE_RFC3264, PJSUA_CALL_HOLD_TYPE_RFC2543} pjsua_call_hold_type;
+
+typedef int pjsua_acc_id;
+
+typedef enum pjsua_destroy_flag {PJSUA_DESTROY_NO_RX_MSG = 1, PJSUA_DESTROY_NO_TX_MSG = 2, PJSUA_DESTROY_NO_NETWORK = PJSUA_DESTROY_NO_RX_MSG | PJSUA_DESTROY_NO_TX_MSG} pjsua_destroy_flag;
+
+typedef enum pjsua_100rel_use {PJSUA_100REL_NOT_USED, PJSUA_100REL_MANDATORY, PJSUA_100REL_OPTIONAL} pjsua_100rel_use;
+
+typedef enum pjsua_sip_timer_use {PJSUA_SIP_TIMER_INACTIVE, PJSUA_SIP_TIMER_OPTIONAL, PJSUA_SIP_TIMER_REQUIRED, PJSUA_SIP_TIMER_ALWAYS} pjsua_sip_timer_use;
+
+typedef enum pjsua_ipv6_use {PJSUA_IPV6_DISABLED, PJSUA_IPV6_ENABLED} pjsua_ipv6_use;
+
+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;
+
+typedef int pjsua_vid_win_id;
+
+typedef int pjsua_call_id;
+
+typedef enum pjsua_med_tp_st {PJSUA_MED_TP_NULL, PJSUA_MED_TP_CREATING, PJSUA_MED_TP_IDLE, PJSUA_MED_TP_INIT, PJSUA_MED_TP_RUNNING, PJSUA_MED_TP_DISABLED} pjsua_med_tp_st;
+
+typedef enum pjsua_call_vid_strm_op {PJSUA_CALL_VID_STRM_NO_OP, PJSUA_CALL_VID_STRM_ADD, PJSUA_CALL_VID_STRM_REMOVE, PJSUA_CALL_VID_STRM_CHANGE_DIR, PJSUA_CALL_VID_STRM_CHANGE_CAP_DEV, PJSUA_CALL_VID_STRM_START_TRANSMIT, PJSUA_CALL_VID_STRM_STOP_TRANSMIT, PJSUA_CALL_VID_STRM_SEND_KEYFRAME} pjsua_call_vid_strm_op;
+
+typedef enum pjsua_vid_req_keyframe_method {PJSUA_VID_REQ_KEYFRAME_SIP_INFO = 1, PJSUA_VID_REQ_KEYFRAME_RTCP_PLI = 2} pjsua_vid_req_keyframe_method;
+
+typedef enum pjsua_call_flag {PJSUA_CALL_UNHOLD = 1, PJSUA_CALL_UPDATE_CONTACT = 2, PJSUA_CALL_INCLUDE_DISABLED_MEDIA = 4} pjsua_call_flag;
+
+typedef enum pjsua_create_media_transport_flag {PJSUA_MED_TP_CLOSE_MEMBER = 1} pjsua_create_media_transport_flag;
+
Index: /pjproject/trunk/pjsip-apps/src/swig/symbols.lst
===================================================================
--- /pjproject/trunk/pjsip-apps/src/swig/symbols.lst	(revision 4704)
+++ /pjproject/trunk/pjsip-apps/src/swig/symbols.lst	(revision 4704)
@@ -0,0 +1,34 @@
+pj/types.h			pj_status_t pj_constants_ pj_uint8_t pj_int32_t pj_uint32_t pj_uint16_t
+pj/file_io.h			pj_file_access
+pj/log.h			pj_log_decoration
+pj/sock_qos.h			pj_qos_type pj_qos_flag pj_qos_wmm_prio pj_qos_params
+pj/ssl_sock.h			pj_ssl_cipher
+
+pjnath/nat_detect.h          	pj_stun_nat_type
+pjnath/turn_session.h          	pj_turn_tp_type
+
+pjmedia/event.h                 pjmedia_event_type
+pjmedia/transport_srtp.h	pjmedia_srtp_use
+pjmedia/vid_stream.h		pjmedia_vid_stream_rc_method
+pjmedia-videodev/videodev.h	pjmedia_vid_dev_index pjmedia_vid_dev_std_index
+pjmedia-audiodev/audiodev.h 	pjmedia_aud_dev_route pjmedia_aud_dev_cap
+pjmedia/wav_port.h              pjmedia_file_writer_option pjmedia_file_player_option
+pjmedia/types.h                 pjmedia_type pjmedia_dir pjmedia_tp_proto
+pjmedia/format.h		pjmedia_format_id
+
+pjsip/sip_auth.h		pjsip_cred_data_type
+pjsip/sip_dialog.h              pjsip_dialog_cap_status
+pjsip/sip_event.h               pjsip_event_id_e
+pjsip/sip_msg.h			pjsip_status_code pjsip_hdr_e
+pjsip/sip_transport.h		pjsip_transport_type_e pjsip_transport_flags_e pjsip_transport_state
+pjsip/sip_transport_tls.h	pjsip_ssl_method
+pjsip/sip_transaction.h         pjsip_tsx_state_e
+pjsip/sip_types.h               pjsip_role_e
+pjsip/sip_util.h                pjsip_redirect_op
+
+pjsip-simple/rpid.h          	pjrpid_activity
+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
Index: /pjproject/trunk/pjsip/build/Makefile
===================================================================
--- /pjproject/trunk/pjsip/build/Makefile	(revision 4703)
+++ /pjproject/trunk/pjsip/build/Makefile	(revision 4704)
@@ -22,4 +22,5 @@
 export PJSIP_SIMPLE_LIB:=libpjsip-simple-$(TARGET_NAME)$(LIBEXT)
 export PJSUA_LIB_LIB:=libpjsua-$(TARGET_NAME)$(LIBEXT)
+export PJSUA2_LIB_LIB=../lib/libpjsua2-$(TARGET_NAME)$(LIBEXT)
 
 ifeq ($(PJ_SHARED_LIBRARIES),)
@@ -33,4 +34,6 @@
 export PJSUA_LIB_SONAME := libpjsua.$(SHLIB_SUFFIX)
 export PJSUA_LIB_SHLIB := $(PJSUA_LIB_SONAME).$(PJ_VERSION_MAJOR)
+export PJSUA2_LIB_SONAME := ../lib/libpjsua2.$(SHLIB_SUFFIX)
+export PJSUA2_LIB_SHLIB := $(PJSUA2_LIB_SONAME).$(PJ_VERSION_MAJOR)
 endif
 
@@ -129,4 +132,29 @@
 
 export CC_OUT CC AR RANLIB HOST_MV HOST_RM HOST_RMDIR HOST_MKDIR OBJEXT LD LDOUT 
+
+###############################################################################
+# Defines for building pjsua2 library
+#
+export PJSUA2_LIB_SRCDIR = ../src/pjsua2
+export PJSUA2_LIB_OBJS += $(OS_OBJS) $(M_OBJS) $(CC_OBJS) $(HOST_OBJS) \
+			   account.o endpoint.o json.o persistent.o types.o \
+			   siptypes.o call.o presence.o media.o
+export PJSUA2_LIB_CFLAGS += $(_CFLAGS) $(PJ_VIDEO_CFLAGS)
+export PJSUA2_LIB_CXXFLAGS = $(PJSUA2_LIB_CFLAGS) 
+
+
+###############################################################################
+# Defines for building pjsua2-test application
+#
+export PJSUA2_TEST_SRCDIR = ../src/pjsua2-test
+export PJSUA2_TEST_OBJS += $(OS_OBJS) $(M_OBJS) $(CC_OBJS) $(HOST_OBJS) \
+			   main.o
+export PJSUA2_TEST_CFLAGS += $(_CFLAGS) $(PJ_VIDEO_CFLAGS)
+export PJSUA2_TEST_CXXFLAGS = $(PJSUA2_LIB_CFLAGS) 
+export PJSUA2_TEST_LDFLAGS += -lpjsua2-$(TARGET_NAME) -lstdc++ $(PJ_LDFLAGS) $(PJ_LDLIBS) $(LDFLAGS)
+export PJSUA2_TEST_EXE := pjsua2-test-$(TARGET_NAME)$(HOST_EXE)
+
+export CC_OUT CC AR RANLIB HOST_MV HOST_RM HOST_RMDIR HOST_MKDIR OBJEXT LD LDOUT 
+
 
 ###############################################################################
@@ -167,5 +195,7 @@
 	   $(PJSIP_UA_LIB) $(PJSIP_UA_SONAME) \
 	   $(PJSUA_LIB_LIB) $(PJSUA_LIB_SONAME) \
-	   $(TEST_EXE)
+	   $(PJSUA2_LIB_LIB) $(PJSUA2_LIB_SONAME) \
+	   $(TEST_EXE) \
+	   $(PJSUA2_TEST_EXE)
 
 all: $(TARGETS)
@@ -191,5 +221,7 @@
 .PHONY: $(PJSIP_SIMPLE_LIB) $(PJSIP_SIMPLE_SONAME)
 .PHONY: $(PJSUA_LIB_LIB) $(PJSUA_LIB_SONAME)
+.PHONY: $(PJSUA2_LIB_LIB) $(PJSUA2_LIB_SONAME)
 .PHONY: $(TEST_EXE)
+.PHONY: $(PJSUA2_TEST_EXE)
 
 pjsip: $(PJSIP_LIB)
@@ -221,4 +253,12 @@
 	$(MAKE) -f $(RULES_MAK) APP=TEST app=pjsip-test $(subst /,$(HOST_PSEP),$(BINDIR)/$@)
 
+pjsua2-test: $(PJSUA2_TEST_EXE)
+$(PJSUA2_TEST_EXE): $(PJSIP_LIB) $(PJSIP_SONAME)
+$(PJSUA2_TEST_EXE): $(PJSIP_UA_LIB) $(PJSIP_UA_SONAME)
+$(PJSUA2_TEST_EXE): $(PJSIP_SIMPLE_LIB) $(PJSIP_SIMPLE_SONAME)
+$(PJSUA2_TEST_EXE): $(PJSUA_LIB_LIB) $(PJSUA_LIB_SONAME)
+$(PJSUA2_TEST_EXE): $(PJSUA2_LIB_LIB) $(PJSUA2_LIB_SONAME)
+	$(MAKE) -f $(RULES_MAK) APP=PJSUA2_TEST app=pjsua2-test $(subst /,$(HOST_PSEP),$(BINDIR)/$@)
+
 .PHONY: pjsip.ko
 pjsip.ko:
@@ -240,4 +280,9 @@
 	echo Making $@
 	$(MAKE) -f $(RULES_MAK) APP=PJSUA_LIB app=pjsua-lib $(subst /,$(HOST_PSEP),$(LIBDIR)/$@)
+
+.PHONY: pjsua2-lib.ko
+pjsua2-lib.ko:
+	echo Making $@
+	$(MAKE) -f $(RULES_MAK) APP=PJSUA2_LIB app=pjsua2-lib $(subst /,$(HOST_PSEP),$(LIBDIR)/$@)
 
 clean:
@@ -246,5 +291,7 @@
 	$(MAKE) -f $(RULES_MAK) APP=PJSIP_SIMPLE app=pjsip-simple $@
 	$(MAKE) -f $(RULES_MAK) APP=PJSUA_LIB app=pjsua-lib $@
+	$(MAKE) -f $(RULES_MAK) APP=PJSUA2_LIB app=pjsua2-lib $@
 	$(MAKE) -f $(RULES_MAK) APP=TEST app=pjsip-test $@
+	$(MAKE) -f $(RULES_MAK) APP=PJSUA2_TEST app=pjsua2-test $@
 
 depend:
@@ -253,6 +300,9 @@
 	$(MAKE) -f $(RULES_MAK) APP=PJSIP_SIMPLE app=pjsip-simple $@
 	$(MAKE) -f $(RULES_MAK) APP=PJSUA_LIB app=pjsua-lib $@
+	$(MAKE) -f $(RULES_MAK) APP=PJSUA2_LIB app=pjsua2-lib $@
 	$(MAKE) -f $(RULES_MAK) APP=TEST app=pjsip-test $@
+	$(MAKE) -f $(RULES_MAK) APP=PJSUA2_TEST app=pjsua2-test $@
 	echo '$(BINDIR)/$(TEST_EXE): $(PJMEDIA_LIB) $(LIBDIR)/$(PJSUA_LIB_LIB) $(LIBDIR)/$(PJSIP_SIMPLE_LIB) $(LIBDIR)/$(PJSIP_UA_LIB) $(LIBDIR)/$(PJSIP_LIB) $(PJNATH_LIB) $(PJLIB_UTIL_LIB) $(PJLIB_LIB)' >> .pjsip-test-$(TARGET_NAME).depend
+	echo '$(BINDIR)/$(PJSUA2_TEST_EXE): $(PJSUA2_LIB_LIB) $(PJMEDIA_LIB) $(LIBDIR)/$(PJSUA_LIB_LIB) $(LIBDIR)/$(PJSIP_SIMPLE_LIB) $(LIBDIR)/$(PJSIP_UA_LIB) $(LIBDIR)/$(PJSIP_LIB) $(PJNATH_LIB) $(PJLIB_UTIL_LIB) $(PJLIB_LIB)' >> .pjsua2-test-$(TARGET_NAME).depend
 
 realclean:
@@ -261,4 +311,6 @@
 	$(subst @@,$(subst /,$(HOST_PSEP),.pjsip-simple-$(TARGET_NAME).depend),$(HOST_RMR))
 	$(subst @@,$(subst /,$(HOST_PSEP),.pjsua-lib-$(TARGET_NAME).depend),$(HOST_RMR))
+	$(subst @@,$(subst /,$(HOST_PSEP),.pjsua2-lib-$(TARGET_NAME).depend),$(HOST_RMR))
+	$(subst @@,$(subst /,$(HOST_PSEP),.pjsua2-test-$(TARGET_NAME).depend),$(HOST_RMR))
 	$(MAKE) -f $(RULES_MAK) APP=PJSIP app=pjsip $@
 	$(MAKE) -f $(RULES_MAK) APP=PJSIP_UA app=pjsip-ua $@
@@ -266,5 +318,7 @@
 	$(MAKE) -f $(RULES_MAK) APP=TEST app=pjsip-test $@
 	$(MAKE) -f $(RULES_MAK) APP=PJSUA_LIB app=pjsua-lib $@
-
-
-
+	$(MAKE) -f $(RULES_MAK) APP=PJSUA2_LIB app=pjsua2-lib $@
+	$(MAKE) -f $(RULES_MAK) APP=PJSUA2_TEST app=pjsua2-test $@
+
+
+
Index: /pjproject/trunk/pjsip/build/pjsua2_lib.vcproj
===================================================================
--- /pjproject/trunk/pjsip/build/pjsua2_lib.vcproj	(revision 4704)
+++ /pjproject/trunk/pjsip/build/pjsua2_lib.vcproj	(revision 4704)
@@ -0,0 +1,3220 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+	ProjectType="Visual C++"
+	Version="8.00"
+	Name="pjsua2_lib"
+	ProjectGUID="{B82CDD25-6903-430E-BD38-D8129A2015C1}"
+	RootNamespace="pjsua2_lib"
+	>
+	<Platforms>
+		<Platform
+			Name="Win32"
+		/>
+		<Platform
+			Name="Pocket PC 2003 (ARMV4)"
+		/>
+		<Platform
+			Name="Smartphone 2003 (ARMV4)"
+		/>
+		<Platform
+			Name="x64"
+		/>
+		<Platform
+			Name="Windows Mobile 6 Standard SDK (ARMV4I)"
+		/>
+		<Platform
+			Name="Windows Mobile 6 Professional SDK (ARMV4I)"
+		/>
+		<Platform
+			Name="Windows Mobile 5.0 Pocket PC SDK (ARMV4I)"
+		/>
+		<Platform
+			Name="Windows Mobile 5.0 Smartphone SDK (ARMV4I)"
+		/>
+	</Platforms>
+	<ToolFiles>
+	</ToolFiles>
+	<Configurations>
+		<Configuration
+			Name="Debug|Win32"
+			ConfigurationType="4"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-win32-common-defaults.vsprops"
+			UseOfMFC="0"
+			ATLMinimizesCRunTimeLibraryUsage="false"
+			CharacterSet="2"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				AdditionalIncludeDirectories="../include,../../pjmedia/include,../../pjlib-util/include,../../pjlib/include,../../pjnath/include"
+				PreprocessorDefinitions="_LIB;"
+				PrecompiledHeaderFile=""
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLibrarianTool"
+				OutputFile="..\lib\pjsua2-lib-$(TargetCPU)-$(PlatformName)-vc$(VSVer)-$(ConfigurationName).lib"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Debug|Pocket PC 2003 (ARMV4)"
+			ConfigurationType="4"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-common-defaults.vsprops"
+			ATLMinimizesCRunTimeLibraryUsage="false"
+			CharacterSet="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				ExecutionBucket="7"
+				AdditionalIncludeDirectories="../include,../../pjmedia/include,../../pjlib-util/include,../../pjlib/include,../../pjnath/include"
+				PreprocessorDefinitions="_LIB;"
+				PrecompiledHeaderFile=""
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLibrarianTool"
+				OutputFile="..\lib\pjsua2-lib-$(TargetCPU)-$(PlatformName)-vc$(VSVer)-$(ConfigurationName).lib"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCCodeSignTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+			<DeploymentTool
+				ForceDirty="-1"
+				RemoteDirectory=""
+				RegisterOutput="0"
+				AdditionalFiles=""
+			/>
+			<DebuggerTool
+			/>
+		</Configuration>
+		<Configuration
+			Name="Debug|Smartphone 2003 (ARMV4)"
+			ConfigurationType="4"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-common-defaults.vsprops"
+			ATLMinimizesCRunTimeLibraryUsage="false"
+			CharacterSet="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				ExecutionBucket="7"
+				AdditionalIncludeDirectories="../include,../../pjmedia/include,../../pjlib-util/include,../../pjlib/include,../../pjnath/include"
+				PreprocessorDefinitions="_LIB;"
+				PrecompiledHeaderFile=""
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLibrarianTool"
+				OutputFile="..\lib\pjsua2-lib-$(TargetCPU)-$(PlatformName)-vc$(VSVer)-$(ConfigurationName).lib"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCCodeSignTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+			<DeploymentTool
+				ForceDirty="-1"
+				RemoteDirectory=""
+				RegisterOutput="0"
+				AdditionalFiles=""
+			/>
+			<DebuggerTool
+			/>
+		</Configuration>
+		<Configuration
+			Name="Debug|x64"
+			ConfigurationType="4"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-win64-common-defaults.vsprops"
+			UseOfMFC="0"
+			ATLMinimizesCRunTimeLibraryUsage="false"
+			CharacterSet="2"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+				TargetEnvironment="3"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				AdditionalIncludeDirectories="../include,../../pjmedia/include,../../pjlib-util/include,../../pjlib/include,../../pjnath/include"
+				PreprocessorDefinitions="_LIB;"
+				PrecompiledHeaderFile=""
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLibrarianTool"
+				OutputFile="..\lib\pjsua2-lib-$(TargetCPU)-$(PlatformName)-vc$(VSVer)-$(ConfigurationName).lib"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Debug|Windows Mobile 6 Standard SDK (ARMV4I)"
+			ConfigurationType="4"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-common-defaults.vsprops"
+			ATLMinimizesCRunTimeLibraryUsage="false"
+			CharacterSet="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				ExecutionBucket="7"
+				AdditionalIncludeDirectories="../include,../../pjmedia/include,../../pjlib-util/include,../../pjlib/include,../../pjnath/include"
+				PreprocessorDefinitions="_LIB;"
+				PrecompiledHeaderFile=""
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLibrarianTool"
+				OutputFile="..\lib\pjsua2-lib-$(TargetCPU)-$(PlatformName)-vc$(VSVer)-$(ConfigurationName).lib"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCCodeSignTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+			<DeploymentTool
+				ForceDirty="-1"
+				RemoteDirectory=""
+				RegisterOutput="0"
+				AdditionalFiles=""
+			/>
+			<DebuggerTool
+			/>
+		</Configuration>
+		<Configuration
+			Name="Debug|Windows Mobile 6 Professional SDK (ARMV4I)"
+			ConfigurationType="4"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-common-defaults.vsprops"
+			ATLMinimizesCRunTimeLibraryUsage="false"
+			CharacterSet="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				ExecutionBucket="7"
+				AdditionalIncludeDirectories="../include,../../pjmedia/include,../../pjlib-util/include,../../pjlib/include,../../pjnath/include"
+				PreprocessorDefinitions="_LIB;"
+				PrecompiledHeaderFile=""
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLibrarianTool"
+				OutputFile="..\lib\pjsua2-lib-$(TargetCPU)-$(PlatformName)-vc$(VSVer)-$(ConfigurationName).lib"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCCodeSignTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+			<DeploymentTool
+				ForceDirty="-1"
+				RemoteDirectory=""
+				RegisterOutput="0"
+				AdditionalFiles=""
+			/>
+			<DebuggerTool
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release|Win32"
+			ConfigurationType="4"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-win32-release-defaults.vsprops"
+			UseOfMFC="0"
+			ATLMinimizesCRunTimeLibraryUsage="false"
+			CharacterSet="2"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				AdditionalIncludeDirectories="../include,../../pjmedia/include,../../pjlib-util/include,../../pjlib/include,../../pjnath/include"
+				PreprocessorDefinitions="_LIB;"
+				PrecompiledHeaderFile=""
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLibrarianTool"
+				OutputFile="..\lib\pjsua2-lib-$(TargetCPU)-$(PlatformName)-vc$(VSVer)-$(ConfigurationName).lib"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release|Pocket PC 2003 (ARMV4)"
+			ConfigurationType="4"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-release-defaults.vsprops"
+			ATLMinimizesCRunTimeLibraryUsage="false"
+			CharacterSet="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				ExecutionBucket="7"
+				AdditionalIncludeDirectories="../include,../../pjmedia/include,../../pjlib-util/include,../../pjlib/include,../../pjnath/include"
+				PreprocessorDefinitions="_LIB;"
+				PrecompiledHeaderFile=""
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLibrarianTool"
+				OutputFile="..\lib\pjsua2-lib-$(TargetCPU)-$(PlatformName)-vc$(VSVer)-$(ConfigurationName).lib"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCCodeSignTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+			<DeploymentTool
+				ForceDirty="-1"
+				RemoteDirectory=""
+				RegisterOutput="0"
+				AdditionalFiles=""
+			/>
+			<DebuggerTool
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release|Smartphone 2003 (ARMV4)"
+			ConfigurationType="4"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-release-defaults.vsprops"
+			ATLMinimizesCRunTimeLibraryUsage="false"
+			CharacterSet="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				ExecutionBucket="7"
+				AdditionalIncludeDirectories="../include,../../pjmedia/include,../../pjlib-util/include,../../pjlib/include,../../pjnath/include"
+				PreprocessorDefinitions="_LIB;"
+				PrecompiledHeaderFile=""
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLibrarianTool"
+				OutputFile="..\lib\pjsua2-lib-$(TargetCPU)-$(PlatformName)-vc$(VSVer)-$(ConfigurationName).lib"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCCodeSignTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+			<DeploymentTool
+				ForceDirty="-1"
+				RemoteDirectory=""
+				RegisterOutput="0"
+				AdditionalFiles=""
+			/>
+			<DebuggerTool
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release|x64"
+			ConfigurationType="4"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-win64-release-defaults.vsprops"
+			UseOfMFC="0"
+			ATLMinimizesCRunTimeLibraryUsage="false"
+			CharacterSet="2"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+				TargetEnvironment="3"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				AdditionalIncludeDirectories="../include,../../pjmedia/include,../../pjlib-util/include,../../pjlib/include,../../pjnath/include"
+				PreprocessorDefinitions="_LIB;"
+				PrecompiledHeaderFile=""
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLibrarianTool"
+				OutputFile="..\lib\pjsua2-lib-$(TargetCPU)-$(PlatformName)-vc$(VSVer)-$(ConfigurationName).lib"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release|Windows Mobile 6 Standard SDK (ARMV4I)"
+			ConfigurationType="4"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-release-defaults.vsprops"
+			ATLMinimizesCRunTimeLibraryUsage="false"
+			CharacterSet="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				ExecutionBucket="7"
+				AdditionalIncludeDirectories="../include,../../pjmedia/include,../../pjlib-util/include,../../pjlib/include,../../pjnath/include"
+				PreprocessorDefinitions="_LIB;"
+				PrecompiledHeaderFile=""
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLibrarianTool"
+				OutputFile="..\lib\pjsua2-lib-$(TargetCPU)-$(PlatformName)-vc$(VSVer)-$(ConfigurationName).lib"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCCodeSignTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+			<DeploymentTool
+				ForceDirty="-1"
+				RemoteDirectory=""
+				RegisterOutput="0"
+				AdditionalFiles=""
+			/>
+			<DebuggerTool
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release|Windows Mobile 6 Professional SDK (ARMV4I)"
+			ConfigurationType="4"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-release-defaults.vsprops"
+			ATLMinimizesCRunTimeLibraryUsage="false"
+			CharacterSet="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				ExecutionBucket="7"
+				AdditionalIncludeDirectories="../include,../../pjmedia/include,../../pjlib-util/include,../../pjlib/include,../../pjnath/include"
+				PreprocessorDefinitions="_LIB;"
+				PrecompiledHeaderFile=""
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLibrarianTool"
+				OutputFile="..\lib\pjsua2-lib-$(TargetCPU)-$(PlatformName)-vc$(VSVer)-$(ConfigurationName).lib"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCCodeSignTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+			<DeploymentTool
+				ForceDirty="-1"
+				RemoteDirectory=""
+				RegisterOutput="0"
+				AdditionalFiles=""
+			/>
+			<DebuggerTool
+			/>
+		</Configuration>
+		<Configuration
+			Name="Debug-Static|Win32"
+			ConfigurationType="4"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-win32-common-defaults.vsprops"
+			UseOfMFC="0"
+			ATLMinimizesCRunTimeLibraryUsage="false"
+			CharacterSet="2"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				AdditionalIncludeDirectories="../include,../../pjmedia/include,../../pjlib-util/include,../../pjlib/include,../../pjnath/include"
+				PreprocessorDefinitions="_LIB;"
+				PrecompiledHeaderFile=""
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLibrarianTool"
+				OutputFile="..\lib\pjsua2-lib-$(TargetCPU)-$(PlatformName)-vc$(VSVer)-$(ConfigurationName).lib"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Debug-Static|Pocket PC 2003 (ARMV4)"
+			ConfigurationType="4"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-common-defaults.vsprops"
+			ATLMinimizesCRunTimeLibraryUsage="false"
+			CharacterSet="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				ExecutionBucket="7"
+				AdditionalIncludeDirectories="../include,../../pjmedia/include,../../pjlib-util/include,../../pjlib/include,../../pjnath/include"
+				PreprocessorDefinitions="_LIB;"
+				PrecompiledHeaderFile=""
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLibrarianTool"
+				OutputFile="..\lib\pjsua2-lib-$(TargetCPU)-$(PlatformName)-vc$(VSVer)-$(ConfigurationName).lib"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCCodeSignTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+			<DeploymentTool
+				ForceDirty="-1"
+				RemoteDirectory=""
+				RegisterOutput="0"
+				AdditionalFiles=""
+			/>
+			<DebuggerTool
+			/>
+		</Configuration>
+		<Configuration
+			Name="Debug-Static|Smartphone 2003 (ARMV4)"
+			ConfigurationType="4"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-common-defaults.vsprops"
+			ATLMinimizesCRunTimeLibraryUsage="false"
+			CharacterSet="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				ExecutionBucket="7"
+				AdditionalIncludeDirectories="../include,../../pjmedia/include,../../pjlib-util/include,../../pjlib/include,../../pjnath/include"
+				PreprocessorDefinitions="_LIB;"
+				PrecompiledHeaderFile=""
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLibrarianTool"
+				OutputFile="..\lib\pjsua2-lib-$(TargetCPU)-$(PlatformName)-vc$(VSVer)-$(ConfigurationName).lib"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCCodeSignTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+			<DeploymentTool
+				ForceDirty="-1"
+				RemoteDirectory=""
+				RegisterOutput="0"
+				AdditionalFiles=""
+			/>
+			<DebuggerTool
+			/>
+		</Configuration>
+		<Configuration
+			Name="Debug-Static|x64"
+			ConfigurationType="4"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-win64-common-defaults.vsprops"
+			UseOfMFC="0"
+			ATLMinimizesCRunTimeLibraryUsage="false"
+			CharacterSet="2"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+				TargetEnvironment="3"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				AdditionalIncludeDirectories="../include,../../pjmedia/include,../../pjlib-util/include,../../pjlib/include,../../pjnath/include"
+				PreprocessorDefinitions="_LIB;"
+				PrecompiledHeaderFile=""
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLibrarianTool"
+				OutputFile="..\lib\pjsua2-lib-$(TargetCPU)-$(PlatformName)-vc$(VSVer)-$(ConfigurationName).lib"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Debug-Static|Windows Mobile 6 Standard SDK (ARMV4I)"
+			ConfigurationType="4"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-common-defaults.vsprops"
+			ATLMinimizesCRunTimeLibraryUsage="false"
+			CharacterSet="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				ExecutionBucket="7"
+				AdditionalIncludeDirectories="../include,../../pjmedia/include,../../pjlib-util/include,../../pjlib/include,../../pjnath/include"
+				PreprocessorDefinitions="_LIB;"
+				PrecompiledHeaderFile=""
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLibrarianTool"
+				OutputFile="..\lib\pjsua2-lib-$(TargetCPU)-$(PlatformName)-vc$(VSVer)-$(ConfigurationName).lib"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCCodeSignTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+			<DeploymentTool
+				ForceDirty="-1"
+				RemoteDirectory=""
+				RegisterOutput="0"
+				AdditionalFiles=""
+			/>
+			<DebuggerTool
+			/>
+		</Configuration>
+		<Configuration
+			Name="Debug-Static|Windows Mobile 6 Professional SDK (ARMV4I)"
+			ConfigurationType="4"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-common-defaults.vsprops"
+			ATLMinimizesCRunTimeLibraryUsage="false"
+			CharacterSet="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				ExecutionBucket="7"
+				AdditionalIncludeDirectories="../include,../../pjmedia/include,../../pjlib-util/include,../../pjlib/include,../../pjnath/include"
+				PreprocessorDefinitions="_LIB;"
+				PrecompiledHeaderFile=""
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLibrarianTool"
+				OutputFile="..\lib\pjsua2-lib-$(TargetCPU)-$(PlatformName)-vc$(VSVer)-$(ConfigurationName).lib"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCCodeSignTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+			<DeploymentTool
+				ForceDirty="-1"
+				RemoteDirectory=""
+				RegisterOutput="0"
+				AdditionalFiles=""
+			/>
+			<DebuggerTool
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release-Dynamic|Win32"
+			ConfigurationType="4"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-win32-release-defaults.vsprops"
+			UseOfMFC="0"
+			ATLMinimizesCRunTimeLibraryUsage="false"
+			CharacterSet="2"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				AdditionalIncludeDirectories="../include,../../pjmedia/include,../../pjlib-util/include,../../pjlib/include,../../pjnath/include"
+				PreprocessorDefinitions="_LIB;"
+				PrecompiledHeaderFile=""
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLibrarianTool"
+				OutputFile="..\lib\pjsua2-lib-$(TargetCPU)-$(PlatformName)-vc$(VSVer)-$(ConfigurationName).lib"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release-Dynamic|Pocket PC 2003 (ARMV4)"
+			ConfigurationType="4"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-release-defaults.vsprops"
+			ATLMinimizesCRunTimeLibraryUsage="false"
+			CharacterSet="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				ExecutionBucket="7"
+				AdditionalIncludeDirectories="../include,../../pjmedia/include,../../pjlib-util/include,../../pjlib/include,../../pjnath/include"
+				PreprocessorDefinitions="_LIB;"
+				PrecompiledHeaderFile=""
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLibrarianTool"
+				OutputFile="..\lib\pjsua2-lib-$(TargetCPU)-$(PlatformName)-vc$(VSVer)-$(ConfigurationName).lib"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCCodeSignTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+			<DeploymentTool
+				ForceDirty="-1"
+				RemoteDirectory=""
+				RegisterOutput="0"
+				AdditionalFiles=""
+			/>
+			<DebuggerTool
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release-Dynamic|Smartphone 2003 (ARMV4)"
+			ConfigurationType="4"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-release-defaults.vsprops"
+			ATLMinimizesCRunTimeLibraryUsage="false"
+			CharacterSet="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				ExecutionBucket="7"
+				AdditionalIncludeDirectories="../include,../../pjmedia/include,../../pjlib-util/include,../../pjlib/include,../../pjnath/include"
+				PreprocessorDefinitions="_LIB;"
+				PrecompiledHeaderFile=""
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLibrarianTool"
+				OutputFile="..\lib\pjsua2-lib-$(TargetCPU)-$(PlatformName)-vc$(VSVer)-$(ConfigurationName).lib"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCCodeSignTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+			<DeploymentTool
+				ForceDirty="-1"
+				RemoteDirectory=""
+				RegisterOutput="0"
+				AdditionalFiles=""
+			/>
+			<DebuggerTool
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release-Dynamic|x64"
+			ConfigurationType="4"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-win64-release-defaults.vsprops"
+			UseOfMFC="0"
+			ATLMinimizesCRunTimeLibraryUsage="false"
+			CharacterSet="2"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+				TargetEnvironment="3"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				AdditionalIncludeDirectories="../include,../../pjmedia/include,../../pjlib-util/include,../../pjlib/include,../../pjnath/include"
+				PreprocessorDefinitions="_LIB;"
+				PrecompiledHeaderFile=""
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLibrarianTool"
+				OutputFile="..\lib\pjsua2-lib-$(TargetCPU)-$(PlatformName)-vc$(VSVer)-$(ConfigurationName).lib"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release-Dynamic|Windows Mobile 6 Standard SDK (ARMV4I)"
+			ConfigurationType="4"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-release-defaults.vsprops"
+			ATLMinimizesCRunTimeLibraryUsage="false"
+			CharacterSet="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				ExecutionBucket="7"
+				AdditionalIncludeDirectories="../include,../../pjmedia/include,../../pjlib-util/include,../../pjlib/include,../../pjnath/include"
+				PreprocessorDefinitions="_LIB;"
+				PrecompiledHeaderFile=""
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLibrarianTool"
+				OutputFile="..\lib\pjsua2-lib-$(TargetCPU)-$(PlatformName)-vc$(VSVer)-$(ConfigurationName).lib"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCCodeSignTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+			<DeploymentTool
+				ForceDirty="-1"
+				RemoteDirectory=""
+				RegisterOutput="0"
+				AdditionalFiles=""
+			/>
+			<DebuggerTool
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release-Dynamic|Windows Mobile 6 Professional SDK (ARMV4I)"
+			ConfigurationType="4"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-release-defaults.vsprops"
+			ATLMinimizesCRunTimeLibraryUsage="false"
+			CharacterSet="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				ExecutionBucket="7"
+				AdditionalIncludeDirectories="../include,../../pjmedia/include,../../pjlib-util/include,../../pjlib/include,../../pjnath/include"
+				PreprocessorDefinitions="_LIB;"
+				PrecompiledHeaderFile=""
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLibrarianTool"
+				OutputFile="..\lib\pjsua2-lib-$(TargetCPU)-$(PlatformName)-vc$(VSVer)-$(ConfigurationName).lib"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCCodeSignTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+			<DeploymentTool
+				ForceDirty="-1"
+				RemoteDirectory=""
+				RegisterOutput="0"
+				AdditionalFiles=""
+			/>
+			<DebuggerTool
+			/>
+		</Configuration>
+		<Configuration
+			Name="Debug-Dynamic|Win32"
+			ConfigurationType="4"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-win32-common-defaults.vsprops"
+			UseOfMFC="0"
+			ATLMinimizesCRunTimeLibraryUsage="false"
+			CharacterSet="2"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				AdditionalIncludeDirectories="../include,../../pjmedia/include,../../pjlib-util/include,../../pjlib/include,../../pjnath/include"
+				PreprocessorDefinitions="_LIB;"
+				PrecompiledHeaderFile=""
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLibrarianTool"
+				OutputFile="..\lib\pjsua2-lib-$(TargetCPU)-$(PlatformName)-vc$(VSVer)-$(ConfigurationName).lib"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Debug-Dynamic|Pocket PC 2003 (ARMV4)"
+			ConfigurationType="4"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-common-defaults.vsprops"
+			ATLMinimizesCRunTimeLibraryUsage="false"
+			CharacterSet="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				ExecutionBucket="7"
+				AdditionalIncludeDirectories="../include,../../pjmedia/include,../../pjlib-util/include,../../pjlib/include,../../pjnath/include"
+				PreprocessorDefinitions="_LIB;"
+				PrecompiledHeaderFile=""
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLibrarianTool"
+				OutputFile="..\lib\pjsua2-lib-$(TargetCPU)-$(PlatformName)-vc$(VSVer)-$(ConfigurationName).lib"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCCodeSignTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+			<DeploymentTool
+				ForceDirty="-1"
+				RemoteDirectory=""
+				RegisterOutput="0"
+				AdditionalFiles=""
+			/>
+			<DebuggerTool
+			/>
+		</Configuration>
+		<Configuration
+			Name="Debug-Dynamic|Smartphone 2003 (ARMV4)"
+			ConfigurationType="4"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-common-defaults.vsprops"
+			ATLMinimizesCRunTimeLibraryUsage="false"
+			CharacterSet="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				ExecutionBucket="7"
+				AdditionalIncludeDirectories="../include,../../pjmedia/include,../../pjlib-util/include,../../pjlib/include,../../pjnath/include"
+				PreprocessorDefinitions="_LIB;"
+				PrecompiledHeaderFile=""
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLibrarianTool"
+				OutputFile="..\lib\pjsua2-lib-$(TargetCPU)-$(PlatformName)-vc$(VSVer)-$(ConfigurationName).lib"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCCodeSignTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+			<DeploymentTool
+				ForceDirty="-1"
+				RemoteDirectory=""
+				RegisterOutput="0"
+				AdditionalFiles=""
+			/>
+			<DebuggerTool
+			/>
+		</Configuration>
+		<Configuration
+			Name="Debug-Dynamic|x64"
+			ConfigurationType="4"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-win64-common-defaults.vsprops"
+			UseOfMFC="0"
+			ATLMinimizesCRunTimeLibraryUsage="false"
+			CharacterSet="2"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+				TargetEnvironment="3"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				AdditionalIncludeDirectories="../include,../../pjmedia/include,../../pjlib-util/include,../../pjlib/include,../../pjnath/include"
+				PreprocessorDefinitions="_LIB;"
+				PrecompiledHeaderFile=""
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLibrarianTool"
+				OutputFile="..\lib\pjsua2-lib-$(TargetCPU)-$(PlatformName)-vc$(VSVer)-$(ConfigurationName).lib"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Debug-Dynamic|Windows Mobile 6 Standard SDK (ARMV4I)"
+			ConfigurationType="4"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-common-defaults.vsprops"
+			ATLMinimizesCRunTimeLibraryUsage="false"
+			CharacterSet="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				ExecutionBucket="7"
+				AdditionalIncludeDirectories="../include,../../pjmedia/include,../../pjlib-util/include,../../pjlib/include,../../pjnath/include"
+				PreprocessorDefinitions="_LIB;"
+				PrecompiledHeaderFile=""
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLibrarianTool"
+				OutputFile="..\lib\pjsua2-lib-$(TargetCPU)-$(PlatformName)-vc$(VSVer)-$(ConfigurationName).lib"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCCodeSignTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+			<DeploymentTool
+				ForceDirty="-1"
+				RemoteDirectory=""
+				RegisterOutput="0"
+				AdditionalFiles=""
+			/>
+			<DebuggerTool
+			/>
+		</Configuration>
+		<Configuration
+			Name="Debug-Dynamic|Windows Mobile 6 Professional SDK (ARMV4I)"
+			ConfigurationType="4"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-common-defaults.vsprops"
+			ATLMinimizesCRunTimeLibraryUsage="false"
+			CharacterSet="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				ExecutionBucket="7"
+				AdditionalIncludeDirectories="../include,../../pjmedia/include,../../pjlib-util/include,../../pjlib/include,../../pjnath/include"
+				PreprocessorDefinitions="_LIB;"
+				PrecompiledHeaderFile=""
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLibrarianTool"
+				OutputFile="..\lib\pjsua2-lib-$(TargetCPU)-$(PlatformName)-vc$(VSVer)-$(ConfigurationName).lib"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCCodeSignTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+			<DeploymentTool
+				ForceDirty="-1"
+				RemoteDirectory=""
+				RegisterOutput="0"
+				AdditionalFiles=""
+			/>
+			<DebuggerTool
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release-Static|Win32"
+			ConfigurationType="4"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-win32-release-defaults.vsprops"
+			UseOfMFC="0"
+			ATLMinimizesCRunTimeLibraryUsage="false"
+			CharacterSet="2"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				AdditionalIncludeDirectories="../include,../../pjmedia/include,../../pjlib-util/include,../../pjlib/include,../../pjnath/include"
+				PreprocessorDefinitions="_LIB;"
+				PrecompiledHeaderFile=""
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLibrarianTool"
+				OutputFile="..\lib\pjsua2-lib-$(TargetCPU)-$(PlatformName)-vc$(VSVer)-$(ConfigurationName).lib"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release-Static|Pocket PC 2003 (ARMV4)"
+			ConfigurationType="4"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-release-defaults.vsprops"
+			ATLMinimizesCRunTimeLibraryUsage="false"
+			CharacterSet="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				ExecutionBucket="7"
+				AdditionalIncludeDirectories="../include,../../pjmedia/include,../../pjlib-util/include,../../pjlib/include,../../pjnath/include"
+				PreprocessorDefinitions="_LIB;"
+				PrecompiledHeaderFile=""
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLibrarianTool"
+				OutputFile="..\lib\pjsua2-lib-$(TargetCPU)-$(PlatformName)-vc$(VSVer)-$(ConfigurationName).lib"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCCodeSignTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+			<DeploymentTool
+				ForceDirty="-1"
+				RemoteDirectory=""
+				RegisterOutput="0"
+				AdditionalFiles=""
+			/>
+			<DebuggerTool
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release-Static|Smartphone 2003 (ARMV4)"
+			ConfigurationType="4"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm2003-release-defaults.vsprops"
+			ATLMinimizesCRunTimeLibraryUsage="false"
+			CharacterSet="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				ExecutionBucket="7"
+				AdditionalIncludeDirectories="../include,../../pjmedia/include,../../pjlib-util/include,../../pjlib/include,../../pjnath/include"
+				PreprocessorDefinitions="_LIB;"
+				PrecompiledHeaderFile=""
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLibrarianTool"
+				OutputFile="..\lib\pjsua2-lib-$(TargetCPU)-$(PlatformName)-vc$(VSVer)-$(ConfigurationName).lib"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCCodeSignTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+			<DeploymentTool
+				ForceDirty="-1"
+				RemoteDirectory=""
+				RegisterOutput="0"
+				AdditionalFiles=""
+			/>
+			<DebuggerTool
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release-Static|x64"
+			ConfigurationType="4"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-win64-release-defaults.vsprops"
+			UseOfMFC="0"
+			ATLMinimizesCRunTimeLibraryUsage="false"
+			CharacterSet="2"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+				TargetEnvironment="3"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				AdditionalIncludeDirectories="../include,../../pjmedia/include,../../pjlib-util/include,../../pjlib/include,../../pjnath/include"
+				PreprocessorDefinitions="_LIB;"
+				PrecompiledHeaderFile=""
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLibrarianTool"
+				OutputFile="..\lib\pjsua2-lib-$(TargetCPU)-$(PlatformName)-vc$(VSVer)-$(ConfigurationName).lib"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release-Static|Windows Mobile 6 Standard SDK (ARMV4I)"
+			ConfigurationType="4"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-release-defaults.vsprops"
+			ATLMinimizesCRunTimeLibraryUsage="false"
+			CharacterSet="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				ExecutionBucket="7"
+				AdditionalIncludeDirectories="../include,../../pjmedia/include,../../pjlib-util/include,../../pjlib/include,../../pjnath/include"
+				PreprocessorDefinitions="_LIB;"
+				PrecompiledHeaderFile=""
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLibrarianTool"
+				OutputFile="..\lib\pjsua2-lib-$(TargetCPU)-$(PlatformName)-vc$(VSVer)-$(ConfigurationName).lib"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCCodeSignTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+			<DeploymentTool
+				ForceDirty="-1"
+				RemoteDirectory=""
+				RegisterOutput="0"
+				AdditionalFiles=""
+			/>
+			<DebuggerTool
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release-Static|Windows Mobile 6 Professional SDK (ARMV4I)"
+			ConfigurationType="4"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm6-release-defaults.vsprops"
+			ATLMinimizesCRunTimeLibraryUsage="false"
+			CharacterSet="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				ExecutionBucket="7"
+				AdditionalIncludeDirectories="../include,../../pjmedia/include,../../pjlib-util/include,../../pjlib/include,../../pjnath/include"
+				PreprocessorDefinitions="_LIB;"
+				PrecompiledHeaderFile=""
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLibrarianTool"
+				OutputFile="..\lib\pjsua2-lib-$(TargetCPU)-$(PlatformName)-vc$(VSVer)-$(ConfigurationName).lib"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCCodeSignTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+			<DeploymentTool
+				ForceDirty="-1"
+				RemoteDirectory=""
+				RegisterOutput="0"
+				AdditionalFiles=""
+			/>
+			<DebuggerTool
+			/>
+		</Configuration>
+		<Configuration
+			Name="Debug|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)"
+			ConfigurationType="4"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm5-common-defaults.vsprops"
+			ATLMinimizesCRunTimeLibraryUsage="false"
+			CharacterSet="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				ExecutionBucket="7"
+				AdditionalIncludeDirectories="../include,../../pjmedia/include,../../pjlib-util/include,../../pjlib/include,../../pjnath/include"
+				PreprocessorDefinitions="_LIB;"
+				PrecompiledHeaderFile=""
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLibrarianTool"
+				OutputFile="..\lib\pjsua-lib-$(TargetCPU)-wm5ppc-vc$(VSVer)-$(ConfigurationName).lib"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCCodeSignTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+			<DeploymentTool
+				ForceDirty="-1"
+				RemoteDirectory=""
+				RegisterOutput="0"
+				AdditionalFiles=""
+			/>
+			<DebuggerTool
+			/>
+		</Configuration>
+		<Configuration
+			Name="Debug|Windows Mobile 5.0 Smartphone SDK (ARMV4I)"
+			ConfigurationType="4"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm5-common-defaults.vsprops"
+			ATLMinimizesCRunTimeLibraryUsage="false"
+			CharacterSet="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				ExecutionBucket="7"
+				AdditionalIncludeDirectories="../include,../../pjmedia/include,../../pjlib-util/include,../../pjlib/include,../../pjnath/include"
+				PreprocessorDefinitions="_LIB;"
+				PrecompiledHeaderFile=""
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLibrarianTool"
+				OutputFile="..\lib\pjsua-lib-$(TargetCPU)-wm5sp-vc$(VSVer)-$(ConfigurationName).lib"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCCodeSignTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+			<DeploymentTool
+				ForceDirty="-1"
+				RemoteDirectory=""
+				RegisterOutput="0"
+				AdditionalFiles=""
+			/>
+			<DebuggerTool
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)"
+			ConfigurationType="4"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm5-release-defaults.vsprops"
+			ATLMinimizesCRunTimeLibraryUsage="false"
+			CharacterSet="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				ExecutionBucket="7"
+				AdditionalIncludeDirectories="../include,../../pjmedia/include,../../pjlib-util/include,../../pjlib/include,../../pjnath/include"
+				PreprocessorDefinitions="_LIB;"
+				PrecompiledHeaderFile=""
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLibrarianTool"
+				OutputFile="..\lib\pjsua-lib-$(TargetCPU)-wm5ppc-vc$(VSVer)-$(ConfigurationName).lib"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCCodeSignTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+			<DeploymentTool
+				ForceDirty="-1"
+				RemoteDirectory=""
+				RegisterOutput="0"
+				AdditionalFiles=""
+			/>
+			<DebuggerTool
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release|Windows Mobile 5.0 Smartphone SDK (ARMV4I)"
+			ConfigurationType="4"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm5-release-defaults.vsprops"
+			ATLMinimizesCRunTimeLibraryUsage="false"
+			CharacterSet="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				ExecutionBucket="7"
+				AdditionalIncludeDirectories="../include,../../pjmedia/include,../../pjlib-util/include,../../pjlib/include,../../pjnath/include"
+				PreprocessorDefinitions="_LIB;"
+				PrecompiledHeaderFile=""
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLibrarianTool"
+				OutputFile="..\lib\pjsua-lib-$(TargetCPU)-wm5sp-vc$(VSVer)-$(ConfigurationName).lib"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCCodeSignTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+			<DeploymentTool
+				ForceDirty="-1"
+				RemoteDirectory=""
+				RegisterOutput="0"
+				AdditionalFiles=""
+			/>
+			<DebuggerTool
+			/>
+		</Configuration>
+		<Configuration
+			Name="Debug-Static|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)"
+			ConfigurationType="4"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm5-common-defaults.vsprops"
+			ATLMinimizesCRunTimeLibraryUsage="false"
+			CharacterSet="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				ExecutionBucket="7"
+				AdditionalIncludeDirectories="../include,../../pjmedia/include,../../pjlib-util/include,../../pjlib/include,../../pjnath/include"
+				PreprocessorDefinitions="_LIB;"
+				PrecompiledHeaderFile=""
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLibrarianTool"
+				OutputFile="..\lib\pjsua-lib-$(TargetCPU)-wm5ppc-vc$(VSVer)-$(ConfigurationName).lib"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCCodeSignTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+			<DeploymentTool
+				ForceDirty="-1"
+				RemoteDirectory=""
+				RegisterOutput="0"
+				AdditionalFiles=""
+			/>
+			<DebuggerTool
+			/>
+		</Configuration>
+		<Configuration
+			Name="Debug-Static|Windows Mobile 5.0 Smartphone SDK (ARMV4I)"
+			ConfigurationType="4"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm5-common-defaults.vsprops"
+			ATLMinimizesCRunTimeLibraryUsage="false"
+			CharacterSet="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				ExecutionBucket="7"
+				AdditionalIncludeDirectories="../include,../../pjmedia/include,../../pjlib-util/include,../../pjlib/include,../../pjnath/include"
+				PreprocessorDefinitions="_LIB;"
+				PrecompiledHeaderFile=""
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLibrarianTool"
+				OutputFile="..\lib\pjsua-lib-$(TargetCPU)-wm5sp-vc$(VSVer)-$(ConfigurationName).lib"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCCodeSignTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+			<DeploymentTool
+				ForceDirty="-1"
+				RemoteDirectory=""
+				RegisterOutput="0"
+				AdditionalFiles=""
+			/>
+			<DebuggerTool
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release-Dynamic|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)"
+			ConfigurationType="4"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm5-release-defaults.vsprops"
+			ATLMinimizesCRunTimeLibraryUsage="false"
+			CharacterSet="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				ExecutionBucket="7"
+				AdditionalIncludeDirectories="../include,../../pjmedia/include,../../pjlib-util/include,../../pjlib/include,../../pjnath/include"
+				PreprocessorDefinitions="_LIB;"
+				PrecompiledHeaderFile=""
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLibrarianTool"
+				OutputFile="..\lib\pjsua-lib-$(TargetCPU)-wm5ppc-vc$(VSVer)-$(ConfigurationName).lib"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCCodeSignTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+			<DeploymentTool
+				ForceDirty="-1"
+				RemoteDirectory=""
+				RegisterOutput="0"
+				AdditionalFiles=""
+			/>
+			<DebuggerTool
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release-Dynamic|Windows Mobile 5.0 Smartphone SDK (ARMV4I)"
+			ConfigurationType="4"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm5-release-defaults.vsprops"
+			ATLMinimizesCRunTimeLibraryUsage="false"
+			CharacterSet="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				ExecutionBucket="7"
+				AdditionalIncludeDirectories="../include,../../pjmedia/include,../../pjlib-util/include,../../pjlib/include,../../pjnath/include"
+				PreprocessorDefinitions="_LIB;"
+				PrecompiledHeaderFile=""
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLibrarianTool"
+				OutputFile="..\lib\pjsua-lib-$(TargetCPU)-wm5sp-vc$(VSVer)-$(ConfigurationName).lib"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCCodeSignTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+			<DeploymentTool
+				ForceDirty="-1"
+				RemoteDirectory=""
+				RegisterOutput="0"
+				AdditionalFiles=""
+			/>
+			<DebuggerTool
+			/>
+		</Configuration>
+		<Configuration
+			Name="Debug-Dynamic|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)"
+			ConfigurationType="4"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm5-common-defaults.vsprops"
+			ATLMinimizesCRunTimeLibraryUsage="false"
+			CharacterSet="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				ExecutionBucket="7"
+				AdditionalIncludeDirectories="../include,../../pjmedia/include,../../pjlib-util/include,../../pjlib/include,../../pjnath/include"
+				PreprocessorDefinitions="_LIB;"
+				PrecompiledHeaderFile=""
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLibrarianTool"
+				OutputFile="..\lib\pjsua-lib-$(TargetCPU)-wm5ppc-vc$(VSVer)-$(ConfigurationName).lib"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCCodeSignTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+			<DeploymentTool
+				ForceDirty="-1"
+				RemoteDirectory=""
+				RegisterOutput="0"
+				AdditionalFiles=""
+			/>
+			<DebuggerTool
+			/>
+		</Configuration>
+		<Configuration
+			Name="Debug-Dynamic|Windows Mobile 5.0 Smartphone SDK (ARMV4I)"
+			ConfigurationType="4"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-debug-dynamic-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm5-common-defaults.vsprops"
+			ATLMinimizesCRunTimeLibraryUsage="false"
+			CharacterSet="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				ExecutionBucket="7"
+				AdditionalIncludeDirectories="../include,../../pjmedia/include,../../pjlib-util/include,../../pjlib/include,../../pjnath/include"
+				PreprocessorDefinitions="_LIB;"
+				PrecompiledHeaderFile=""
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLibrarianTool"
+				OutputFile="..\lib\pjsua-lib-$(TargetCPU)-wm5sp-vc$(VSVer)-$(ConfigurationName).lib"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCCodeSignTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+			<DeploymentTool
+				ForceDirty="-1"
+				RemoteDirectory=""
+				RegisterOutput="0"
+				AdditionalFiles=""
+			/>
+			<DebuggerTool
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release-Static|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)"
+			ConfigurationType="4"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm5-release-defaults.vsprops"
+			ATLMinimizesCRunTimeLibraryUsage="false"
+			CharacterSet="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				ExecutionBucket="7"
+				AdditionalIncludeDirectories="../include,../../pjmedia/include,../../pjlib-util/include,../../pjlib/include,../../pjnath/include"
+				PreprocessorDefinitions="_LIB;"
+				PrecompiledHeaderFile=""
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLibrarianTool"
+				OutputFile="..\lib\pjsua-lib-$(TargetCPU)-wm5ppc-vc$(VSVer)-$(ConfigurationName).lib"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCCodeSignTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+			<DeploymentTool
+				ForceDirty="-1"
+				RemoteDirectory=""
+				RegisterOutput="0"
+				AdditionalFiles=""
+			/>
+			<DebuggerTool
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release-Static|Windows Mobile 5.0 Smartphone SDK (ARMV4I)"
+			ConfigurationType="4"
+			InheritedPropertySheets="..\..\build\vs\pjproject-vs8-release-static-defaults.vsprops;..\..\build\vs\pjproject-vs8-wm5-release-defaults.vsprops"
+			ATLMinimizesCRunTimeLibraryUsage="false"
+			CharacterSet="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				ExecutionBucket="7"
+				AdditionalIncludeDirectories="../include,../../pjmedia/include,../../pjlib-util/include,../../pjlib/include,../../pjnath/include"
+				PreprocessorDefinitions="_LIB;"
+				PrecompiledHeaderFile=""
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLibrarianTool"
+				OutputFile="..\lib\pjsua-lib-$(TargetCPU)-wm5sp-vc$(VSVer)-$(ConfigurationName).lib"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCCodeSignTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+			<DeploymentTool
+				ForceDirty="-1"
+				RemoteDirectory=""
+				RegisterOutput="0"
+				AdditionalFiles=""
+			/>
+			<DebuggerTool
+			/>
+		</Configuration>
+	</Configurations>
+	<References>
+	</References>
+	<Files>
+		<Filter
+			Name="Source Files"
+			Filter="cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+			>
+			<File
+				RelativePath="..\src\pjsua2\account.cpp"
+				>
+			</File>
+			<File
+				RelativePath="..\src\pjsua2\call.cpp"
+				>
+			</File>
+			<File
+				RelativePath="..\src\pjsua2\endpoint.cpp"
+				>
+			</File>
+			<File
+				RelativePath="..\src\pjsua2\json.cpp"
+				>
+			</File>
+			<File
+				RelativePath="..\src\pjsua2\media.cpp"
+				>
+			</File>
+			<File
+				RelativePath="..\src\pjsua2\persistent.cpp"
+				>
+			</File>
+			<File
+				RelativePath="..\src\pjsua2\presence.cpp"
+				>
+			</File>
+			<File
+				RelativePath="..\src\pjsua2\siptypes.cpp"
+				>
+			</File>
+			<File
+				RelativePath="..\src\pjsua2\types.cpp"
+				>
+			</File>
+			<File
+				RelativePath="..\src\pjsua2\util.hpp"
+				>
+			</File>
+		</Filter>
+		<Filter
+			Name="Header Files"
+			Filter="h;hpp;hxx;hm;inl"
+			>
+			<File
+				RelativePath="..\include\pjsua2\account.hpp"
+				>
+			</File>
+			<File
+				RelativePath="..\include\pjsua2\call.hpp"
+				>
+			</File>
+			<File
+				RelativePath="..\include\pjsua2\config.hpp"
+				>
+			</File>
+			<File
+				RelativePath="..\include\pjsua2\doxygen.hpp"
+				>
+			</File>
+			<File
+				RelativePath="..\include\pjsua2\endpoint.hpp"
+				>
+			</File>
+			<File
+				RelativePath="..\include\pjsua2\json.hpp"
+				>
+			</File>
+			<File
+				RelativePath="..\include\pjsua2\media.hpp"
+				>
+			</File>
+			<File
+				RelativePath="..\include\pjsua2\persistent.hpp"
+				>
+			</File>
+			<File
+				RelativePath="..\include\pjsua2\presence.hpp"
+				>
+			</File>
+			<File
+				RelativePath="..\include\pjsua2\siptypes.hpp"
+				>
+			</File>
+			<File
+				RelativePath="..\include\pjsua2\types.hpp"
+				>
+			</File>
+		</Filter>
+	</Files>
+	<Globals>
+	</Globals>
+</VisualStudioProject>
Index: /pjproject/trunk/pjsip/docs/doxygen.cfg
===================================================================
--- /pjproject/trunk/pjsip/docs/doxygen.cfg	(revision 4703)
+++ /pjproject/trunk/pjsip/docs/doxygen.cfg	(revision 4704)
@@ -357,5 +357,5 @@
 # *.h++ *.idl *.odl
 
-FILE_PATTERNS          = *.h
+FILE_PATTERNS          = *.h *.hpp
 
 # The RECURSIVE tag can be used to turn specify whether or not subdirectories 
Index: /pjproject/trunk/pjsip/include/pjsip-simple/evsub.h
===================================================================
--- /pjproject/trunk/pjsip/include/pjsip-simple/evsub.h	(revision 4703)
+++ /pjproject/trunk/pjsip/include/pjsip-simple/evsub.h	(revision 4704)
@@ -59,5 +59,5 @@
  * in state_str member of the susbcription structure.
  */
-enum pjsip_evsub_state
+typedef enum pjsip_evsub_state
 {
     PJSIP_EVSUB_STATE_NULL,	 /**< State is NULL.			    */
@@ -71,10 +71,5 @@
 				      Application can query the state by 
 				      calling #pjsip_evsub_get_state_name().*/
-};
-
-/**
- * @see pjsip_evsub_state
- */
-typedef enum pjsip_evsub_state pjsip_evsub_state;
+} pjsip_evsub_state;
 
 
Index: /pjproject/trunk/pjsip/include/pjsua-lib/pjsua.h
===================================================================
--- /pjproject/trunk/pjsip/include/pjsua-lib/pjsua.h	(revision 4703)
+++ /pjproject/trunk/pjsip/include/pjsua-lib/pjsua.h	(revision 4704)
@@ -252,5 +252,8 @@
 
 /** Constant to identify invalid ID for all sorts of IDs. */
-#define PJSUA_INVALID_ID	    (-1)
+enum pjsua_invalid_id_const_
+{
+    PJSUA_INVALID_ID = -1
+};
 
 /** Disabled features temporarily for media reorganization */
@@ -1881,4 +1884,24 @@
 
 /**
+ * Register a thread to poll for events. This function should be
+ * called by an external worker thread, and it will block polling
+ * for events until the library is destroyed.
+ *
+ * @return 		PJ_SUCCESS if things are working correctly
+ * 			or an error polling cannot be done for some
+ * 			reason.
+ */
+PJ_DECL(pj_status_t) pjsua_register_worker_thread(const char *name);
+
+
+/**
+ * Signal all worker threads to quit. This will only wait until internal
+ * threads are done. For external threads, application must perform
+ * its own waiting for the external threads to quit from
+ * pjsua_register_worker_thread() function.
+ */
+PJ_DECL(void) pjsua_stop_worker_threads(void);
+
+/**
  * Create memory pool to be used by the application. Once application
  * finished using the pool, it must be released with pj_pool_release().
@@ -5778,4 +5801,7 @@
     pj_str_t		name;
 
+    /** Format. */
+    pjmedia_format	format;
+
     /** Clock rate. */
     unsigned		clock_rate;
@@ -5789,4 +5815,10 @@
     /** Bits per sample */
     unsigned		bits_per_sample;
+
+    /** Tx level adjustment. */
+    float		tx_level_adj;
+
+    /** Rx level adjustment. */
+    float		rx_level_adj;
 
     /** Number of listeners in the array. */
Index: /pjproject/trunk/pjsip/include/pjsua2.hpp
===================================================================
--- /pjproject/trunk/pjsip/include/pjsua2.hpp	(revision 4704)
+++ /pjproject/trunk/pjsip/include/pjsua2.hpp	(revision 4704)
@@ -0,0 +1,29 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2008-2013 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 __PJSUA2_HPP__
+#define __PJSUA2_HPP__
+
+#include <pjsua2/endpoint.hpp>
+#include <pjsua2/account.hpp>
+#include <pjsua2/call.hpp>
+#include <pjsua2/presence.hpp>
+#include <pjsua2/media.hpp>
+#include <pjsua2/json.hpp>
+
+#endif
Index: /pjproject/trunk/pjsip/include/pjsua2/account.hpp
===================================================================
--- /pjproject/trunk/pjsip/include/pjsua2/account.hpp	(revision 4704)
+++ /pjproject/trunk/pjsip/include/pjsua2/account.hpp	(revision 4704)
@@ -0,0 +1,1557 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2013 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 __PJSUA2_ACCOUNT_HPP__
+#define __PJSUA2_ACCOUNT_HPP__
+
+/**
+ * @file pjsua2/account.hpp
+ * @brief PJSUA2 Account operations
+ */
+#include <pjsua-lib/pjsua.h>
+#include <pjsua2/persistent.hpp>
+#include <pjsua2/presence.hpp>
+#include <pjsua2/siptypes.hpp>
+
+/** PJSUA2 API is inside pj namespace */
+namespace pj
+{
+
+/**
+ * @defgroup PJSUA2_ACC Account
+ * @ingroup PJSUA2_Ref
+ * @{
+ */
+
+using std::string;
+
+/**
+ * Account registration config. This will be specified in AccountConfig.
+ */
+struct AccountRegConfig : public PersistentObject
+{
+    /**
+     * This is the URL to be put in the request URI for the registration,
+     * and will look something like "sip:serviceprovider".
+     *
+     * This field should be specified if registration is desired. If the
+     * value is empty, no account registration will be performed.
+     */
+    string		registrarUri;
+
+    /**
+     * Specify whether the account should register as soon as it is
+     * added to the UA. Application can set this to PJ_FALSE and control
+     * the registration manually with pjsua_acc_set_registration().
+     *
+     * Default: True
+     */
+    bool		registerOnAdd;
+
+    /**
+     * The optional custom SIP headers to be put in the registration
+     * request.
+     */
+    SipHeaderVector	headers;
+
+    /**
+     * Optional interval for registration, in seconds. If the value is zero,
+     * default interval will be used (PJSUA_REG_INTERVAL, 300 seconds).
+     */
+    unsigned		timeoutSec;
+
+    /**
+     * Specify interval of auto registration retry upon registration failure
+     * (including caused by transport problem), in second. Set to 0 to
+     * disable auto re-registration. Note that if the registration retry
+     * occurs because of transport failure, the first retry will be done
+     * after \a firstRetryIntervalSec seconds instead. Also note that
+     * the interval will be randomized slightly by approximately +/- ten
+     * seconds to avoid all clients re-registering at the same time.
+     *
+     * See also \a firstRetryIntervalSec setting.
+     *
+     * Default: PJSUA_REG_RETRY_INTERVAL
+     */
+    unsigned		retryIntervalSec;
+
+    /**
+     * This specifies the interval for the first registration retry. The
+     * registration retry is explained in \a retryIntervalSec. Note that
+     * the value here will also be randomized by +/- ten seconds.
+     *
+     * Default: 0
+     */
+    unsigned		firstRetryIntervalSec;
+
+    /**
+     * Specify the number of seconds to refresh the client registration
+     * before the registration expires.
+     *
+     * Default: PJSIP_REGISTER_CLIENT_DELAY_BEFORE_REFRESH, 5 seconds
+     */
+    unsigned		delayBeforeRefreshSec;
+
+    /**
+     * Specify whether calls of the configured account should be dropped
+     * after registration failure and an attempt of re-registration has
+     * also failed.
+     *
+     * Default: FALSE (disabled)
+     */
+    bool		dropCallsOnFail;
+
+    /**
+     * Specify the maximum time to wait for unregistration requests to
+     * complete during library shutdown sequence.
+     *
+     * Default: PJSUA_UNREG_TIMEOUT
+     */
+    unsigned		unregWaitSec;
+
+    /**
+     * Specify how the registration uses the outbound and account proxy
+     * settings. This controls if and what Route headers will appear in
+     * the REGISTER request of this account. The value is bitmask combination
+     * of PJSUA_REG_USE_OUTBOUND_PROXY and PJSUA_REG_USE_ACC_PROXY bits.
+     * If the value is set to 0, the REGISTER request will not use any proxy
+     * (i.e. it will not have any Route headers).
+     *
+     * Default: 3 (PJSUA_REG_USE_OUTBOUND_PROXY | PJSUA_REG_USE_ACC_PROXY)
+     */
+    unsigned		proxyUse;
+
+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);
+
+};
+
+/** Array of SIP credentials */
+typedef std::vector<AuthCredInfo> AuthCredInfoVector;
+
+/**
+ * Various SIP settings for the account. This will be specified in
+ * AccountConfig.
+ */
+struct AccountSipConfig : public PersistentObject
+{
+    /**
+     * Array of credentials. If registration is desired, normally there should
+     * be at least one credential specified, to successfully authenticate
+     * against the service provider. More credentials can be specified, for
+     * example when the requests are expected to be challenged by the
+     * proxies in the route set.
+     */
+    AuthCredInfoVector	authCreds;
+
+    /**
+     * Array of proxy servers to visit for outgoing requests. Each of the
+     * entry is translated into one Route URI.
+     */
+    StringVector	proxies;
+
+    /**
+     * Optional URI to be put as Contact for this account. It is recommended
+     * that this field is left empty, so that the value will be calculated
+     * automatically based on the transport address.
+     */
+    string		contactForced;
+
+    /**
+     * Additional parameters that will be appended in the Contact header
+     * for this account. This will affect the Contact header in all SIP
+     * messages sent on behalf of this account, including but not limited to
+     * REGISTER, INVITE, and SUBCRIBE requests or responses.
+     *
+     * The parameters should be preceeded by semicolon, and all strings must
+     * be properly escaped. Example:
+     *	 ";my-param=X;another-param=Hi%20there"
+     */
+    string		contactParams;
+
+    /**
+     * Additional URI parameters that will be appended in the Contact URI
+     * for this account. This will affect the Contact URI in all SIP
+     * messages sent on behalf of this account, including but not limited to
+     * REGISTER, INVITE, and SUBCRIBE requests or responses.
+     *
+     * The parameters should be preceeded by semicolon, and all strings must
+     * be properly escaped. Example:
+     *	 ";my-param=X;another-param=Hi%20there"
+     */
+    string		contactUriParams;
+
+
+    /**
+     * If this flag is set, the authentication client framework will
+     * send an empty Authorization header in each initial request.
+     * Default is no.
+     */
+    bool		authInitialEmpty;
+
+    /**
+     * Specify the algorithm to use when empty Authorization header
+     * is to be sent for each initial request (see above)
+     */
+    string		authInitialAlgorithm;
+
+    /**
+     * Optionally bind this account to specific transport. This normally is
+     * not a good idea, as account should be able to send requests using
+     * any available transports according to the destination. But some
+     * application may want to have explicit control over the transport to
+     * use, so in that case it can set this field.
+     *
+     * Default: -1 (PJSUA_INVALID_ID)
+     *
+     * @see Account::setTransport()
+     */
+    TransportId		transportId;
+
+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);
+};
+
+/**
+ * Account's call settings. This will be specified in AccountConfig.
+ */
+struct AccountCallConfig : public PersistentObject
+{
+    /**
+     * Specify how to offer call hold to remote peer. Please see the
+     * documentation on pjsua_call_hold_type for more info.
+     *
+     * Default: PJSUA_CALL_HOLD_TYPE_DEFAULT
+     */
+    pjsua_call_hold_type holdType;
+
+    /**
+     * Specify how support for reliable provisional response (100rel/
+     * PRACK) should be used for all sessions in this account. See the
+     * documentation of pjsua_100rel_use enumeration for more info.
+     *
+     * Default: PJSUA_100REL_NOT_USED
+     */
+    pjsua_100rel_use	prackUse;
+
+    /**
+     * Specify the usage of Session Timers for all sessions. See the
+     * pjsua_sip_timer_use for possible values.
+     *
+     * Default: PJSUA_SIP_TIMER_OPTIONAL
+     */
+    pjsua_sip_timer_use	timerUse;
+
+    /**
+     * Specify minimum Session Timer expiration period, in seconds.
+     * Must not be lower than 90. Default is 90.
+     */
+    unsigned		timerMinSESec;
+
+    /**
+     * Specify Session Timer expiration period, in seconds.
+     * Must not be lower than timerMinSE. Default is 1800.
+     */
+    unsigned		timerSessExpiresSec;
+
+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);
+};
+
+/**
+ * Account presence config. This will be specified in AccountConfig.
+ */
+struct AccountPresConfig : public PersistentObject
+{
+    /**
+     * The optional custom SIP headers to be put in the presence
+     * subscription request.
+     */
+    SipHeaderVector	headers;
+
+    /**
+     * If this flag is set, the presence information of this account will
+     * be PUBLISH-ed to the server where the account belongs.
+     *
+     * Default: PJ_FALSE
+     */
+    bool		publishEnabled;
+
+    /**
+     * 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
+     * on the PUBLISH request if there is another PUBLISH transaction still
+     * in progress.
+     *
+     * Default: PJSIP_PUBLISHC_QUEUE_REQUEST (TRUE)
+     */
+    bool		publishQueue;
+
+    /**
+     * Maximum time to wait for unpublication transaction(s) to complete
+     * during shutdown process, before sending unregistration. The library
+     * tries to wait for the unpublication (un-PUBLISH) to complete before
+     * sending REGISTER request to unregister the account, during library
+     * shutdown process. If the value is set too short, it is possible that
+     * the unregistration is sent before unpublication completes, causing
+     * unpublication request to fail.
+     *
+     * Value is in milliseconds.
+     *
+     * Default: PJSUA_UNPUBLISH_MAX_WAIT_TIME_MSEC (2000)
+     */
+    unsigned		publishShutdownWaitMsec;
+
+    /**
+     * Optional PIDF tuple ID for outgoing PUBLISH and NOTIFY. If this value
+     * is not specified, a random string will be used.
+     */
+    string		pidfTupleId;
+
+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);
+};
+
+/**
+ * Account MWI (Message Waiting Indication) settings. This will be specified
+ * in AccountConfig.
+ */
+struct AccountMwiConfig : public PersistentObject
+{
+    /**
+     * Subscribe to message waiting indication events (RFC 3842).
+     *
+     * See also UaConfig.mwiUnsolicitedEnabled setting.
+     *
+     * Default: FALSE
+     */
+    bool		enabled;
+
+    /**
+     * Specify the default expiration time (in seconds) for Message
+     * Waiting Indication (RFC 3842) event subscription. This must not
+     * be zero.
+     *
+     * Default: PJSIP_MWI_DEFAULT_EXPIRES (3600)
+     */
+    unsigned		expirationSec;
+
+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);
+};
+
+/**
+ * Account's NAT (Network Address Translation) settings. This will be
+ * specified in AccountConfig.
+ */
+struct AccountNatConfig : public PersistentObject
+{
+    /**
+     * Control the use of STUN for the SIP signaling.
+     *
+     * Default: PJSUA_STUN_USE_DEFAULT
+     */
+    pjsua_stun_use 	sipStunUse;
+
+    /**
+     * Control the use of STUN for the media transports.
+     *
+     * Default: PJSUA_STUN_USE_DEFAULT
+     */
+    pjsua_stun_use 	mediaStunUse;
+
+    /**
+     * Enable ICE for the media transport.
+     *
+     * Default: False
+     */
+    bool		iceEnabled;
+
+    /**
+     * Set the maximum number of ICE host candidates.
+     *
+     * Default: -1 (maximum not set)
+     */
+    int			iceMaxHostCands;
+
+    /**
+     * Specify whether to use aggressive nomination.
+     *
+     * Default: True
+     */
+    bool		iceAggressiveNomination;
+
+    /**
+     * For controlling agent if it uses regular nomination, specify the delay
+     * to perform nominated check (connectivity check with USE-CANDIDATE
+     * attribute) after all components have a valid pair.
+     *
+     * Default value is PJ_ICE_NOMINATED_CHECK_DELAY.
+     */
+    unsigned		iceNominatedCheckDelayMsec;
+
+    /**
+     * For a controlled agent, specify how long it wants to wait (in
+     * milliseconds) for the controlling agent to complete sending
+     * connectivity check with nominated flag set to true for all components
+     * after the controlled agent has found that all connectivity checks in
+     * its checklist have been completed and there is at least one successful
+     * (but not nominated) check for every component.
+     *
+     * Default value for this option is
+     * ICE_CONTROLLED_AGENT_WAIT_NOMINATION_TIMEOUT. Specify -1 to disable
+     * this timer.
+     */
+    int			iceWaitNominationTimeoutMsec;
+
+    /**
+     * Disable RTCP component.
+     *
+     * Default: False
+     */
+    bool		iceNoRtcp;
+
+    /**
+     * Always send re-INVITE/UPDATE after ICE negotiation regardless of whether
+     * the default ICE transport address is changed or not. When this is set
+     * to False, re-INVITE/UPDATE will be sent only when the default ICE
+     * transport address is changed.
+     *
+     * Default: yes
+     */
+    bool		iceAlwaysUpdate;
+
+    /**
+     * Enable TURN candidate in ICE.
+     */
+    bool		turnEnabled;
+
+    /**
+     * Specify TURN domain name or host name, in in "DOMAIN:PORT" or
+     * "HOST:PORT" format.
+     */
+    string		turnServer;
+
+    /**
+     * Specify the connection type to be used to the TURN server. Valid
+     * values are PJ_TURN_TP_UDP or PJ_TURN_TP_TCP.
+     *
+     * Default: PJ_TURN_TP_UDP
+     */
+    pj_turn_tp_type	turnConnType;
+
+    /**
+     * Specify the username to authenticate with the TURN server.
+     */
+    string		turnUserName;
+
+    /**
+     * Specify the type of password. Currently this must be zero to
+     * indicate plain-text password will be used in the password.
+     */
+    int			turnPasswordType;
+
+    /**
+     * Specify the password to authenticate with the TURN server.
+     */
+    string		turnPassword;
+
+    /**
+     * This option is used to update the transport address and the Contact
+     * header of REGISTER request. When this option is  enabled, the library
+     * will keep track of the public IP address from the response of REGISTER
+     * request. Once it detects that the address has changed, it will
+     * unregister current Contact, update the Contact with transport address
+     * learned from Via header, and register a new Contact to the registrar.
+     * This will also update the public name of UDP transport if STUN is
+     * configured.
+     *
+     * See also contactRewriteMethod field.
+     *
+     * Default: TRUE
+     */
+    int			contactRewriteUse;
+
+    /**
+     * Specify how Contact update will be done with the registration, if
+     * \a contactRewriteEnabled is enabled. The value is bitmask combination of
+     * \a pjsua_contact_rewrite_method. See also pjsua_contact_rewrite_method.
+     *
+     * Value PJSUA_CONTACT_REWRITE_UNREGISTER(1) is the legacy behavior.
+     *
+     * Default value: PJSUA_CONTACT_REWRITE_METHOD
+     *   (PJSUA_CONTACT_REWRITE_NO_UNREG | PJSUA_CONTACT_REWRITE_ALWAYS_UPDATE)
+     */
+    int			contactRewriteMethod;
+
+    /**
+     * This option is used to overwrite the "sent-by" field of the Via header
+     * for outgoing messages with the same interface address as the one in
+     * the REGISTER request, as long as the request uses the same transport
+     * instance as the previous REGISTER request.
+     *
+     * Default: TRUE
+     */
+    int			viaRewriteUse;
+
+    /**
+     * This option controls whether the IP address in SDP should be replaced
+     * with the IP address found in Via header of the REGISTER response, ONLY
+     * when STUN and ICE are not used. If the value is FALSE (the original
+     * behavior), then the local IP address will be used. If TRUE, and when
+     * STUN and ICE are disabled, then the IP address found in registration
+     * response will be used.
+     *
+     * Default: PJ_FALSE (no)
+     */
+    int			sdpNatRewriteUse;
+
+    /**
+     * Control the use of SIP outbound feature. SIP outbound is described in
+     * RFC 5626 to enable proxies or registrar to send inbound requests back
+     * to UA using the same connection initiated by the UA for its
+     * registration. This feature is highly useful in NAT-ed deployemtns,
+     * hence it is enabled by default.
+     *
+     * Note: currently SIP outbound can only be used with TCP and TLS
+     * transports. If UDP is used for the registration, the SIP outbound
+     * feature will be silently ignored for the account.
+     *
+     * Default: TRUE
+     */
+    int			sipOutboundUse;
+
+    /**
+     * Specify SIP outbound (RFC 5626) instance ID to be used by this
+     * account. If empty, an instance ID will be generated based on
+     * the hostname of this agent. If application specifies this parameter, the
+     * value will look like "<urn:uuid:00000000-0000-1000-8000-AABBCCDDEEFF>"
+     * without the double-quotes.
+     *
+     * Default: empty
+     */
+    string		sipOutboundInstanceId;
+
+    /**
+     * Specify SIP outbound (RFC 5626) registration ID. The default value
+     * is empty, which would cause the library to automatically generate
+     * a suitable value.
+     *
+     * Default: empty
+     */
+    string		sipOutboundRegId;
+
+    /**
+     * Set the interval for periodic keep-alive transmission for this account.
+     * If this value is zero, keep-alive will be disabled for this account.
+     * The keep-alive transmission will be sent to the registrar's address,
+     * after successful registration.
+     *
+     * Default: 15 (seconds)
+     */
+    unsigned		udpKaIntervalSec;
+
+    /**
+     * Specify the data to be transmitted as keep-alive packets.
+     *
+     * Default: CR-LF
+     */
+    string		udpKaData;
+
+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);
+};
+
+/**
+ * Account media config (applicable for both audio and video). This will be
+ * specified in AccountConfig.
+ */
+struct AccountMediaConfig : public PersistentObject
+{
+    /**
+     * Media transport (RTP) configuration.
+     */
+    TransportConfig	transportConfig;
+
+    /**
+     * If remote sends SDP answer containing more than one format or codec in
+     * the media line, send re-INVITE or UPDATE with just one codec to lock
+     * which codec to use.
+     *
+     * Default: True (Yes).
+     */
+    bool		lockCodecEnabled;
+
+    /**
+     * Specify whether stream keep-alive and NAT hole punching with
+     * non-codec-VAD mechanism (see PJMEDIA_STREAM_ENABLE_KA) is enabled
+     * for this account.
+     *
+     * Default: False
+     */
+    bool		streamKaEnabled;
+
+    /**
+     * Specify whether secure media transport should be used for this account.
+     * Valid values are PJMEDIA_SRTP_DISABLED, PJMEDIA_SRTP_OPTIONAL, and
+     * PJMEDIA_SRTP_MANDATORY.
+     *
+     * Default: PJSUA_DEFAULT_USE_SRTP
+     */
+    pjmedia_srtp_use	srtpUse;
+
+    /**
+     * Specify whether SRTP requires secure signaling to be used. This option
+     * is only used when \a use_srtp option above is non-zero.
+     *
+     * Valid values are:
+     *	0: SRTP does not require secure signaling
+     *	1: SRTP requires secure transport such as TLS
+     *	2: SRTP requires secure end-to-end transport (SIPS)
+     *
+     * Default: PJSUA_DEFAULT_SRTP_SECURE_SIGNALING
+     */
+    int			srtpSecureSignaling;
+
+    /**
+     * Specify whether IPv6 should be used on media. Default is not used.
+     */
+    pjsua_ipv6_use	ipv6Use;
+
+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);
+};
+
+/**
+ * Account video config. This will be specified in AccountConfig.
+ */
+struct AccountVideoConfig : public PersistentObject
+{
+    /**
+     * Specify whether incoming video should be shown to screen by default.
+     * This applies to incoming call (INVITE), incoming re-INVITE, and
+     * incoming UPDATE requests.
+     *
+     * Regardless of this setting, application can detect incoming video
+     * by implementing \a on_call_media_state() callback and enumerating
+     * the media stream(s) with pjsua_call_get_info(). Once incoming
+     * video is recognised, application may retrieve the window associated
+     * with the incoming video and show or hide it with
+     * pjsua_vid_win_set_show().
+     *
+     * Default: False
+     */
+    bool			autoShowIncoming;
+
+    /**
+     * Specify whether outgoing video should be activated by default when
+     * making outgoing calls and/or when incoming video is detected. This
+     * applies to incoming and outgoing calls, incoming re-INVITE, and
+     * incoming UPDATE. If the setting is non-zero, outgoing video
+     * transmission will be started as soon as response to these requests
+     * is sent (or received).
+     *
+     * Regardless of the value of this setting, application can start and
+     * stop outgoing video transmission with pjsua_call_set_vid_strm().
+     *
+     * Default: False
+     */
+    bool			autoTransmitOutgoing;
+
+    /**
+     * Specify video window's flags. The value is a bitmask combination of
+     * pjmedia_vid_dev_wnd_flag.
+     *
+     * Default: 0
+     */
+    unsigned			windowFlags;
+
+    /**
+     * Specify the default capture device to be used by this account. If
+     * vidOutAutoTransmit is enabled, this device will be used for
+     * capturing video.
+     *
+     * Default: PJMEDIA_VID_DEFAULT_CAPTURE_DEV
+     */
+    pjmedia_vid_dev_index 	defaultCaptureDevice;
+
+    /**
+     * Specify the default rendering device to be used by this account.
+     *
+     * Default: PJMEDIA_VID_DEFAULT_RENDER_DEV
+     */
+    pjmedia_vid_dev_index 	defaultRenderDevice;
+
+    /**
+     * Rate control method.
+     *
+     * Default: PJMEDIA_VID_STREAM_RC_SIMPLE_BLOCKING.
+     */
+    pjmedia_vid_stream_rc_method rateControlMethod;
+
+    /**
+     * Upstream/outgoing bandwidth. If this is set to zero, the video stream
+     * will use codec maximum bitrate setting.
+     *
+     * Default: 0 (follow codec maximum bitrate).
+     */
+    unsigned			rateControlBandwidth;
+
+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);
+};
+
+/**
+ * Account configuration.
+ */
+struct AccountConfig : public PersistentObject
+{
+    /**
+     * Account priority, which is used to control the order of matching
+     * incoming/outgoing requests. The higher the number means the higher
+     * the priority is, and the account will be matched first.
+     */
+    int			priority;
+
+    /**
+     * The Address of Record or AOR, that is full SIP URL that identifies the
+     * account. The value can take name address or URL format, and will look
+     * something like "sip:account@serviceprovider".
+     *
+     * This field is mandatory.
+     */
+    string		idUri;
+
+    /**
+     * Registration settings.
+     */
+    AccountRegConfig	regConfig;
+
+    /**
+     * SIP settings.
+     */
+    AccountSipConfig	sipConfig;
+
+    /**
+     * Call settings.
+     */
+    AccountCallConfig	callConfig;
+
+    /**
+     * Presence settings.
+     */
+    AccountPresConfig	presConfig;
+
+    /**
+     * MWI (Message Waiting Indication) settings.
+     */
+    AccountMwiConfig	mwiConfig;
+
+    /**
+     * NAT settings.
+     */
+    AccountNatConfig	natConfig;
+
+    /**
+     * Media settings (applicable for both audio and video).
+     */
+    AccountMediaConfig	mediaConfig;
+
+    /**
+     * Video settings.
+     */
+    AccountVideoConfig	videoConfig;
+
+public:
+    /**
+     * Default constructor will initialize with default values.
+     */
+    AccountConfig();
+
+    /**
+     * This will return a temporary pjsua_acc_config instance, which contents
+     * are only valid as long as this AccountConfig structure remains valid
+     * AND no modifications are done to it AND no further toPj() function call
+     * is made. Any call to toPj() function will invalidate the content of
+     * temporary pjsua_acc_config that was returned by the previous call.
+     */
+    void toPj(pjsua_acc_config &cfg) const;
+
+    /**
+     * Initialize from pjsip.
+     */
+    void fromPj(const pjsua_acc_config &prm, const pjsua_media_config *mcfg);
+
+    /**
+     * 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);
+};
+
+
+/**
+ * Account information. Application can query the account information
+ * by calling Account::getInfo().
+ */
+struct AccountInfo
+{
+    /**
+     * The account ID.
+     */
+    pjsua_acc_id	id;
+
+    /**
+     * Flag to indicate whether this is the default account.
+     */
+    bool		isDefault;
+
+    /**
+     * Account URI
+     */
+    string		uri;
+
+    /**
+     * Flag to tell whether this account has registration setting
+     * (reg_uri is not empty).
+     */
+    bool		regIsConfigured;
+
+    /**
+     * Flag to tell whether this account is currently registered
+     * (has active registration session).
+     */
+    bool		regIsActive;
+
+    /**
+     * An up to date expiration interval for account registration session.
+     */
+    int			regExpiresSec;
+
+    /**
+     * Last registration status code. If status code is zero, the account
+     * is currently not registered. Any other value indicates the SIP
+     * status code of the registration.
+     */
+    pjsip_status_code	regStatus;
+
+    /**
+     * String describing the registration status.
+     */
+    string		regStatusText;
+
+    /**
+     * Last registration error code. When the status field contains a SIP
+     * status code that indicates a registration failure, last registration
+     * error code contains the error code that causes the failure. In any
+     * other case, its value is zero.
+     */
+    pj_status_t		regLastErr;
+
+    /**
+     * Presence online status for this account.
+     */
+    bool		onlineStatus;
+
+    /**
+     * Presence online status text.
+     */
+    string		onlineStatusText;
+
+public:
+    /** Import from pjsip data */
+    void fromPj(const pjsua_acc_info &pai);
+};
+
+/**
+ * This structure contains parameters for onIncomingCall() account callback.
+ */
+struct OnIncomingCallParam
+{
+    /**
+     * The library call ID allocated for the new call.
+     */
+    int			callId;
+
+    /**
+     * The incoming INVITE request.
+     */
+    SipRxData		rdata;
+};
+
+/**
+ * This structure contains parameters for onRegStarted() account callback.
+ */
+struct OnRegStartedParam
+{
+    /**
+     * True for registration and False for unregistration.
+     */
+    bool renew;
+};
+
+/**
+ * This structure contains parameters for onRegState() account callback.
+ */
+struct OnRegStateParam
+{
+    /**
+     * Registration operation status.
+     */
+    pj_status_t		status;
+
+    /**
+     * SIP status code received.
+     */
+    pjsip_status_code	code;
+
+    /**
+     * SIP reason phrase received.
+     */
+    string		reason;
+
+    /**
+     * The incoming message.
+     */
+    SipRxData		rdata;
+
+    /**
+     * Next expiration interval.
+     */
+    int			expiration;
+};
+
+/**
+ * This structure contains parameters for onIncomingSubscribe() callback.
+ */
+struct OnIncomingSubscribeParam
+{
+    /**
+     * Server presence subscription instance. If application delays
+     * the acceptance of the request, it will need to specify this object
+     * when calling Account::presNotify().
+     */
+    void	       *srvPres;
+
+    /**
+     *  Sender URI.
+     */
+    string		fromUri;
+
+    /**
+     * The incoming message.
+     */
+    SipRxData		rdata;
+
+    /**
+     * The status code to respond to the request. The default value is 200.
+     * Application may set this to other final status code to accept or
+     * reject the request.
+     */
+    pjsip_status_code	code;
+
+    /**
+     * The reason phrase to respond to the request.
+     */
+    string		reason;
+
+    /**
+     * Additional data to be sent with the response, if any.
+     */
+    SipTxOption		txOption;
+};
+
+/**
+ * Parameters for onInstantMessage() account callback.
+ */
+struct OnInstantMessageParam
+{
+    /**
+     * Sender From URI.
+     */
+    string		fromUri;
+
+    /**
+     * To URI of the request.
+     */
+    string		toUri;
+
+    /**
+     * Contact URI of the sender.
+     */
+    string		contactUri;
+
+    /**
+     * MIME type of the message body.
+     */
+    string		contentType;
+
+    /**
+     * The message body.
+     */
+    string		msgBody;
+
+    /**
+     * The whole message.
+     */
+    SipRxData		rdata;
+};
+
+/**
+ * Parameters for onInstantMessageStatus() account callback.
+ */
+struct OnInstantMessageStatusParam
+{
+    /**
+     * Token or a user data that was associated with the pager
+     * transmission.
+     */
+    Token		userData;
+
+    /**
+     * Destination URI.
+     */
+    string		toUri;
+
+    /**
+     * The message body.
+     */
+    string		msgBody;
+
+    /**
+     * The SIP status code of the transaction.
+     */
+    pjsip_status_code	code;
+
+    /**
+     * The reason phrase of the transaction.
+     */
+    string		reason;
+
+    /**
+     * The incoming response that causes this callback to be called.
+     * If the transaction fails because of time out or transport error,
+     * the content will be empty.
+     */
+    SipRxData		rdata;
+};
+
+/**
+ * Parameters for onTypingIndication() account callback.
+ */
+struct OnTypingIndicationParam
+{
+    /**
+     * Sender/From URI.
+     */
+    string		fromUri;
+
+    /**
+     * To URI.
+     */
+    string		toUri;
+
+    /**
+     * The Contact URI.
+     */
+    string		contactUri;
+
+    /**
+     * Boolean to indicate if sender is typing.
+     */
+    bool		isTyping;
+
+    /**
+     * The whole message buffer.
+     */
+    SipRxData		rdata;
+};
+
+/**
+ * Parameters for onMwiInfo() account callback.
+ */
+struct OnMwiInfoParam
+{
+    /**
+     * MWI subscription state.
+     */
+    pjsip_evsub_state	state;
+
+    /**
+     * The whole message buffer.
+     */
+    SipRxData		rdata;
+};
+
+/**
+ * Parameters for presNotify() account method.
+ */
+struct PresNotifyParam
+{
+    /**
+     * Server presence subscription instance.
+     */
+    void	       *srvPres;
+
+    /**
+     * Server presence subscription state to set.
+     */
+    pjsip_evsub_state	state;
+    
+    /**
+     * Optionally specify the state string name, if state is not "active",
+     * "pending", or "terminated".
+     */
+    string		stateStr;
+
+    /**
+     * If the new state is PJSIP_EVSUB_STATE_TERMINATED, optionally specify
+     * the termination reason.
+     */
+    string		reason;
+
+    /**
+     * If the new state is PJSIP_EVSUB_STATE_TERMINATED, this specifies
+     * whether the NOTIFY request should contain message body containing
+     * account's presence information.
+     */
+    bool		withBody;
+
+    /**
+     * Optional list of headers to be sent with the NOTIFY request.
+     */
+    SipTxOption		txOption;
+};
+
+
+/**
+ * Wrapper class for Buddy matching algo.
+ *
+ * Default algo is a simple substring lookup of search-token in the
+ * Buddy URIs, with case sensitive. Application can implement its own
+ * matching algo by overriding this class and specifying its instance
+ * in Account::findBuddy().
+ */
+class FindBuddyMatch
+{
+public:
+    /**
+     * Default algo implementation.
+     */
+    virtual bool match(const string &token, const Buddy &buddy)
+    {
+	BuddyInfo bi = buddy.getInfo();
+	return bi.uri.find(token) != string::npos;
+    }
+
+    /**
+     * Destructor.
+     */
+    virtual ~FindBuddyMatch() {}
+};
+
+
+/**
+ * Account.
+ */
+class Account
+{
+public:
+    /**
+     * Constructor.
+     */
+    Account();
+
+    /**
+     * Destructor. Note that if the account is deleted, it will also delete
+     * the corresponding account in the PJSUA-LIB.
+     */
+    virtual ~Account();
+
+    /**
+     * Create the account.
+     *
+     * @param cfg		The account config.
+     * @param make_default	Make this the default account.
+     */
+    void create(const AccountConfig &cfg,
+                bool make_default=false) throw(Error);
+
+    /**
+     * Modify the account to use the specified account configuration.
+     * Depending on the changes, this may cause unregistration or
+     * reregistration on the account.
+     *
+     * @param cfg 		New account config to be applied to the
+     * 				account.
+     */
+    void modify(const AccountConfig &cfg) throw(Error);
+
+    /**
+     * Check if this account is still valid.
+     *
+     * @return			True if it is.
+     */
+    bool isValid() const;
+
+    /**
+     * Set this as default account to be used when incoming and outgoing
+     * requests don't match any accounts.
+     *
+     * @return			PJ_SUCCESS on success.
+     */
+    void setDefault() throw(Error);
+
+    /**
+     * Check if this account is the default account. Default account will be
+     * used for incoming and outgoing requests that don't match any other
+     * accounts.
+     *
+     * @return			True if this is the default account.
+     */
+    bool isDefault() const;
+
+    /**
+     * Get PJSUA-LIB account ID or index associated with this account.
+     *
+     * @return			Integer greater than or equal to zero.
+     */
+    int getId() const;
+
+    /**
+     * Get the Account class for the specified account Id.
+     *
+     * @param acc_id		The account ID to lookup
+     *
+     * @return			The Account instance or NULL if not found.
+     */
+    static Account *lookup(int acc_id);
+
+    /**
+     * Get account info.
+     *
+     * @return			Account info.
+     */
+    AccountInfo getInfo() const throw(Error);
+
+    /**
+     * Update registration or perform unregistration. Application normally
+     * only needs to call this function if it wants to manually update the
+     * registration or to unregister from the server.
+     *
+     * @param renew		If False, this will start unregistration
+     * 				process.
+     */
+    void setRegistration(bool renew) throw(Error);
+
+    /**
+     * Set or modify account's presence online status to be advertised to
+     * remote/presence subscribers. This would trigger the sending of
+     * outgoing NOTIFY request if there are server side presence subscription
+     * for this account, and/or outgoing PUBLISH if presence publication is
+     * enabled for this account.
+     *
+     * @param pres_st		Presence online status.
+     */
+    void setOnlineStatus(const PresenceStatus &pres_st) throw(Error);
+
+    /**
+     * Lock/bind this account to a specific transport/listener. Normally
+     * application shouldn't need to do this, as transports will be selected
+     * automatically by the library according to the destination.
+     *
+     * When account is locked/bound to a specific transport, all outgoing
+     * requests from this account will use the specified transport (this
+     * includes SIP registration, dialog (call and event subscription), and
+     * out-of-dialog requests such as MESSAGE).
+     *
+     * Note that transport id may be specified in AccountConfig too.
+     *
+     * @param tp_id		The transport ID.
+     */
+    void setTransport(TransportId tp_id) throw(Error);
+
+    /**
+     * Send NOTIFY to inform account presence status or to terminate server
+     * side presence subscription. If application wants to reject the incoming
+     * request, it should set the param \a PresNotifyParam.state to
+     * PJSIP_EVSUB_STATE_TERMINATED.
+     *
+     * @param prm		The sending NOTIFY parameter.
+     */
+    void presNotify(const PresNotifyParam &prm) throw(Error);
+    
+    /**
+     * Enumerate all buddies of the account.
+     *
+     * @return			The buddy list.
+     */
+    const BuddyVector& enumBuddies() const throw(Error);
+
+    /**
+     * Find a buddy in the buddy list with the specified URI. 
+     *
+     * Exception: if buddy is not found, PJ_ENOTFOUND will be thrown.
+     *
+     * @param uri		The buddy URI.
+     * @param buddy_match	The buddy match algo.
+     *
+     * @return			The pointer to buddy.
+     */
+    Buddy* findBuddy(string uri, FindBuddyMatch *buddy_match = NULL) const
+		    throw(Error);
+
+    /**
+     * An internal function to add a Buddy to Account buddy list.
+     * This function must never be used by application.
+     */
+    void addBuddy(Buddy *buddy);
+
+    /**
+     * An internal function to remove a Buddy from Account buddy list.
+     * This function must never be used by application.
+     */
+    void removeBuddy(Buddy *buddy);
+
+public:
+    /*
+     * Callbacks
+     */
+    /**
+     * Notify application on incoming call.
+     *
+     * @param prm	Callback parameter.
+     */
+    virtual void onIncomingCall(OnIncomingCallParam &prm)
+    { PJ_UNUSED_ARG(prm); }
+
+    /**
+     * Notify application when registration or unregistration has been
+     * initiated. Note that this only notifies the initial registration
+     * and unregistration. Once registration session is active, subsequent
+     * refresh will not cause this callback to be called.
+     *
+     * @param prm	    Callback parameter.
+     */
+    virtual void onRegStarted(OnRegStartedParam &prm)
+    { PJ_UNUSED_ARG(prm); }
+
+    /**
+     * Notify application when registration status has changed.
+     * Application may then query the account info to get the
+     * registration details.
+     *
+     * @param prm	    Callback parameter.
+     */
+    virtual void onRegState(OnRegStateParam &prm)
+    { PJ_UNUSED_ARG(prm); }
+
+    /**
+     * Notification when incoming SUBSCRIBE request is received. Application
+     * may use this callback to authorize the incoming subscribe request
+     * (e.g. ask user permission if the request should be granted).
+     *
+     * If this callback is not implemented, all incoming presence subscription
+     * requests will be accepted.
+     *
+     * If this callback is implemented, application has several choices on
+     * what to do with the incoming request:
+     *	- it may reject the request immediately by specifying non-200 class
+     *    final response in the IncomingSubscribeParam.code parameter.
+     *	- it may immediately accept the request by specifying 200 as the
+     *	  IncomingSubscribeParam.code parameter. This is the default value if
+     *	  application doesn't set any value to the IncomingSubscribeParam.code
+     *	  parameter. In this case, the library will automatically send NOTIFY
+     *	  request upon returning from this callback.
+     *	- it may delay the processing of the request, for example to request
+     *    user permission whether to accept or reject the request. In this
+     *	  case, the application MUST set the IncomingSubscribeParam.code
+     *	  argument to 202, then IMMEDIATELY calls presNotify() with
+     *	  state PJSIP_EVSUB_STATE_PENDING and later calls presNotify()
+     *    again to accept or reject the subscription request.
+     *
+     * Any IncomingSubscribeParam.code other than 200 and 202 will be treated
+     * as 200.
+     *
+     * Application MUST return from this callback immediately (e.g. it must
+     * not block in this callback while waiting for user confirmation).
+     *
+     * @param prm	    Callback parameter.
+     */
+    virtual void onIncomingSubscribe(OnIncomingSubscribeParam &prm)
+    { PJ_UNUSED_ARG(prm); }
+
+    /**
+     * Notify application on incoming instant message or pager (i.e. MESSAGE
+     * request) that was received outside call context.
+     *
+     * @param prm	    Callback parameter.
+     */
+    virtual void onInstantMessage(OnInstantMessageParam &prm)
+    { PJ_UNUSED_ARG(prm); }
+
+    /**
+     * Notify application about the delivery status of outgoing pager/instant
+     * message (i.e. MESSAGE) request.
+     *
+     * @param prm	    Callback parameter.
+     */
+    virtual void onInstantMessageStatus(OnInstantMessageStatusParam &prm)
+    { PJ_UNUSED_ARG(prm); }
+
+    /**
+     * Notify application about typing indication.
+     *
+     * @param prm	    Callback parameter.
+     */
+    virtual void onTypingIndication(OnTypingIndicationParam &prm)
+    { PJ_UNUSED_ARG(prm); }
+
+    /**
+     * Notification about MWI (Message Waiting Indication) status change.
+     * This callback can be called upon the status change of the
+     * SUBSCRIBE request (for example, 202/Accepted to SUBSCRIBE is received)
+     * or when a NOTIFY reqeust is received.
+     *
+     * @param prm	    Callback parameter.
+     */
+    virtual void onMwiInfo(OnMwiInfoParam &prm)
+    { PJ_UNUSED_ARG(prm); }
+
+protected:
+    friend class Endpoint;
+
+private:
+    pjsua_acc_id 	 id;
+    string		 tmpReason;	// for saving response's reason
+    BuddyVector		 buddyList;
+};
+
+/**
+ * @}  // PJSUA2_ACC
+ */
+
+} // namespace pj
+
+#endif	/* __PJSUA2_ACCOUNT_HPP__ */
+
Index: /pjproject/trunk/pjsip/include/pjsua2/call.hpp
===================================================================
--- /pjproject/trunk/pjsip/include/pjsua2/call.hpp	(revision 4704)
+++ /pjproject/trunk/pjsip/include/pjsua2/call.hpp	(revision 4704)
@@ -0,0 +1,1713 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2012-2013 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 __PJSUA2_CALL_HPP__
+#define __PJSUA2_CALL_HPP__
+
+/**
+ * @file pjsua2/call.hpp
+ * @brief PJSUA2 Call manipulation
+ */
+#include <pjsua-lib/pjsua.h>
+#include <pjsua2/media.hpp>
+
+/** PJSUA2 API is inside pj namespace */
+namespace pj
+{
+
+/**
+ * @defgroup PJSUA2_CALL Call
+ * @ingroup PJSUA2_Ref
+ */
+
+/**
+ * @defgroup PJSUA2_Call_Data_Structure Call Related Types
+ * @ingroup PJSUA2_DS
+ * @{
+ */
+
+using std::string;
+using std::vector;
+
+//////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Codec parameters, corresponds to pjmedia_codec_param or
+ * pjmedia_vid_codec_param.
+ */
+typedef void *CodecParam;
+
+/**
+ * Media stream, corresponds to pjmedia_stream
+ */
+typedef void *MediaStream;
+
+/**
+ * Media transport, corresponds to pjmedia_transport
+ */
+typedef void *MediaTransport;
+
+/**
+ * This structure describes statistics state.
+ */
+struct MathStat
+{
+    int n;      /**< number of samples  */
+    int max;    /**< maximum value      */
+    int min;    /**< minimum value      */
+    int last;   /**< last value         */
+    int mean;   /**< mean               */
+
+public:
+    /**
+     * Default constructor
+     */
+    MathStat();
+    
+    /**
+     * Convert from pjsip
+     */
+    void fromPj(const pj_math_stat &prm);
+};
+
+/**
+ * Unidirectional RTP stream statistics.
+ */
+struct RtcpStreamStat
+{
+    TimeValue	    update;	/**< Time of last update.		    */
+    unsigned	    updateCount;/**< Number of updates (to calculate avg)   */
+    unsigned	    pkt;	/**< Total number of packets		    */
+    unsigned	    bytes;	/**< Total number of payload/bytes	    */
+    unsigned	    discard;	/**< Total number of discarded packets.	    */
+    unsigned	    loss;	/**< Total number of packets lost	    */
+    unsigned	    reorder;	/**< Total number of out of order packets   */
+    unsigned	    dup;	/**< Total number of duplicates packets	    */
+    
+    MathStat        lossPeriodUsec; /**< Loss period statistics 	    */
+
+    struct {
+        unsigned    burst;	/**< Burst/sequential packet lost detected  */
+        unsigned    random;	/**< Random packet lost detected.	    */
+    } lossType;                 /**< Types of loss detected.                */
+    
+    MathStat        jitterUsec;	/**< Jitter statistics                      */
+    
+public:
+    /**
+     * Convert from pjsip
+     */
+    void fromPj(const pjmedia_rtcp_stream_stat &prm);
+};
+
+/**
+ * RTCP SDES structure.
+ */
+struct RtcpSdes
+{
+    string	cname;		/**< RTCP SDES type CNAME.	*/
+    string	name;		/**< RTCP SDES type NAME.	*/
+    string	email;		/**< RTCP SDES type EMAIL.	*/
+    string	phone;		/**< RTCP SDES type PHONE.	*/
+    string	loc;		/**< RTCP SDES type LOC.	*/
+    string	tool;		/**< RTCP SDES type TOOL.	*/
+    string	note;		/**< RTCP SDES type NOTE.	*/
+
+public:
+    /**
+     * Convert from pjsip
+     */
+    void fromPj(const pjmedia_rtcp_sdes &prm);
+};
+    
+/**
+ * Bidirectional RTP stream statistics.
+ */
+struct RtcpStat
+{
+    TimeValue           start;          /**< Time when session was created  */
+    
+    RtcpStreamStat      txStat;         /**< Encoder stream statistics.	    */
+    RtcpStreamStat      rxStat;         /**< Decoder stream statistics.	    */
+    
+    MathStat            rttUsec;        /**< Round trip delay statistic.    */
+    
+    pj_uint32_t		rtpTxLastTs;    /**< Last TX RTP timestamp.         */
+    pj_uint16_t		rtpTxLastSeq;   /**< Last TX RTP sequence.          */
+
+    MathStat            rxIpdvUsec;     /**< Statistics of IP packet delay
+                                             variation in receiving
+                                             direction. It is only used when
+                                             PJMEDIA_RTCP_STAT_HAS_IPDV is
+                                             set to non-zero.               */
+
+    MathStat            rxRawJitterUsec;/**< Statistic of raw jitter in
+                                             receiving direction. It is only 
+                                             used when
+                                             PJMEDIA_RTCP_STAT_HAS_RAW_JITTER
+                                             is set to non-zero.            */
+    
+    RtcpSdes            peerSdes;       /**< Peer SDES.			    */
+
+public:
+    /**
+     * Convert from pjsip
+     */
+    void fromPj(const pjmedia_rtcp_stat &prm);
+};
+
+/**
+ * This structure describes jitter buffer state.
+ */
+struct JbufState
+{
+    /* Setting */
+    unsigned	frameSize;	    /**< Individual frame size, in bytes.   */
+    unsigned	minPrefetch;	    /**< Minimum allowed prefetch, in frms. */
+    unsigned	maxPrefetch;	    /**< Maximum allowed prefetch, in frms. */
+    
+    /* Status */
+    unsigned	burst;		    /**< Current burst level, in frames	    */
+    unsigned	prefetch;	    /**< Current prefetch value, in frames  */
+    unsigned	size;		    /**< Current buffer size, in frames.    */
+    
+    /* Statistic */
+    unsigned	avgDelayMsec;	    /**< Average delay, in ms.		    */
+    unsigned	minDelayMsec;	    /**< Minimum delay, in ms.		    */
+    unsigned	maxDelayMsec;	    /**< Maximum delay, in ms.		    */
+    unsigned	devDelayMsec;	    /**< Standard deviation of delay, in ms.*/
+    unsigned	avgBurst;	    /**< Average burst, in frames.	    */
+    unsigned	lost;		    /**< Number of lost frames.		    */
+    unsigned	discard;	    /**< Number of discarded frames.	    */
+    unsigned	empty;		    /**< Number of empty on GET events.	    */
+    
+public:
+    /**
+     * Convert from pjsip
+     */
+    void fromPj(const pjmedia_jb_state &prm);
+};
+
+/**
+ * This structure describes SDP session description. It corresponds to the
+ * pjmedia_sdp_session structure.
+ */
+struct SdpSession
+{
+    /**
+     * The whole SDP as a string.
+     */
+    string  wholeSdp;
+    
+    /**
+     * Pointer to its original pjmedia_sdp_session. Only valid when the struct
+     * is converted from PJSIP's pjmedia_sdp_session.
+     */
+    void   *pjSdpSession;
+
+public:
+    /**
+     * Convert from pjsip
+     */
+    void fromPj(const pjmedia_sdp_session &sdp);
+};
+
+/**
+ * This structure describes a media format changed event.
+ */
+struct MediaFmtChangedEvent
+{
+    unsigned newWidth;      /**< The new width.     */
+    unsigned newHeight;     /**< The new height.    */
+};
+
+/**
+ * This structure describes a media event. It corresponds to the
+ * pjmedia_event structure.
+ */
+struct MediaEvent
+{
+    /**
+     * The event type.
+     */
+    pjmedia_event_type          type;
+
+    /**
+     * Additional data/parameters about the event. The type of data
+     * will be specific to the event type being reported.
+     */
+    union {
+	/**
+         * Media format changed event data.
+         */
+	MediaFmtChangedEvent    fmtChanged;
+        
+	/**
+         * Pointer to storage to user event data, if it's outside
+	 * this struct
+	 */
+	GenericData		ptr;
+    } data;
+    
+    /**
+     * Pointer to original pjmedia_event. Only valid when the struct
+     * is converted from PJSIP's pjmedia_event.
+     */
+    void                       *pjMediaEvent;
+
+public:
+    /**
+     * Convert from pjsip
+     */
+    void fromPj(const pjmedia_event &ev);
+};
+
+/**
+ * This structure describes media transport informations. It corresponds to the
+ * pjmedia_transport_info structure.
+ */
+struct MediaTransportInfo
+{
+    /**
+     * Remote address where RTP originated from.
+     */
+    SocketAddress   srcRtpName;
+
+    /**
+     * Remote address where RTCP originated from.
+     */
+    SocketAddress   srcRtcpName;
+    
+public:
+    /**
+     * Convert from pjsip
+     */
+    void fromPj(const pjmedia_transport_info &info);
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Call settings.
+ */
+struct CallSetting
+{
+    /**
+     * Bitmask of pjsua_call_flag constants.
+     *
+     * Default: PJSUA_CALL_INCLUDE_DISABLED_MEDIA
+     */
+    unsigned	    flag;
+    
+    /**
+     * This flag controls what methods to request keyframe are allowed on
+     * the call. Value is bitmask of pjsua_vid_req_keyframe_method.
+     *
+     * Default: PJSUA_VID_REQ_KEYFRAME_SIP_INFO |
+     *          PJSUA_VID_REQ_KEYFRAME_RTCP_PLI
+     */
+    unsigned	    reqKeyframeMethod;
+    
+    /**
+     * Number of simultaneous active audio streams for this call. Setting
+     * this to zero will disable audio in this call.
+     *
+     * Default: 1
+     */
+    unsigned        audioCount;
+    
+    /**
+     * Number of simultaneous active video streams for this call. Setting
+     * this to zero will disable video in this call.
+     *
+     * Default: 1 (if video feature is enabled, otherwise it is zero)
+     */
+    unsigned        videoCount;
+    
+public:
+    /**
+     * Default constructor initializes with empty or default values.
+     */
+    CallSetting(pj_bool_t useDefaultValues = false);
+
+    /**
+     * Check if the settings are set with empty values.
+     *
+     * @return      True if the settings are empty.
+     */
+    bool isEmpty() const;
+
+    /**
+     * Convert from pjsip
+     */
+    void fromPj(const pjsua_call_setting &prm);
+
+    /**
+     * Convert to pjsip
+     */
+    pjsua_call_setting toPj() const;
+};
+
+/**
+ * Call media information.
+ */
+struct CallMediaInfo
+{
+    /**
+     * Media index in SDP.
+     */
+    unsigned                index;
+    
+    /**
+     * Media type.
+     */
+    pjmedia_type            type;
+    
+    /**
+     * Media direction.
+     */
+    pjmedia_dir             dir;
+    
+    /**
+     * Call media status.
+     */
+    pjsua_call_media_status status;
+    
+    /**
+     * The conference port number for the call. Only valid if the media type
+     * is audio.
+     */
+    int                     audioConfSlot;
+    
+    /**
+     * The window id for incoming video, if any, or
+     * PJSUA_INVALID_ID. Only valid if the media type is video.
+     */
+    pjsua_vid_win_id	    videoIncomingWindowId;
+    
+    /**
+     * The video capture device for outgoing transmission, if any,
+     * or PJMEDIA_VID_INVALID_DEV. Only valid if the media type is video.
+     */
+    pjmedia_vid_dev_index   videoCapDev;
+    
+public:
+    /**
+     * Default constructor
+     */
+    CallMediaInfo();
+    
+    /**
+     * Convert from pjsip
+     */
+    void fromPj(const pjsua_call_media_info &prm);
+};
+    
+/** Array of call media info */
+typedef std::vector<CallMediaInfo> CallMediaInfoVector;
+
+/**
+ * Call information. Application can query the call information
+ * by calling Call::getInfo().
+ */
+struct CallInfo
+{
+    /**
+     * Call identification.
+     */
+    pjsua_call_id	id;
+    
+    /**
+     * Initial call role (UAC == caller)
+     */
+    pjsip_role_e	role;
+    
+    /**
+     * The account ID where this call belongs.
+     */
+    pjsua_acc_id	accId;
+    
+    /**
+     * Local URI
+     */
+    string		localUri;
+    
+    /**
+     * Local Contact
+     */
+    string		localContact;
+    
+    /**
+     * Remote URI
+     */
+    string		remoteUri;
+    
+    /**
+     * Remote contact
+     */
+    string		remoteContact;
+    
+    /**
+     * Dialog Call-ID string.
+     */
+    string		callIdString;
+    
+    /**
+     * Call setting
+     */
+    CallSetting         setting;
+    
+    /**
+     * Call state
+     */
+    pjsip_inv_state	state;
+    
+    /**
+     * Text describing the state
+     */
+    string		stateText;
+    
+    /**
+     * Last status code heard, which can be used as cause code
+     */
+    pjsip_status_code	lastStatusCode;
+    
+    /**
+     * The reason phrase describing the last status.
+     */
+    string		lastReason;
+    
+    /**
+     * Array of active media information.
+     */
+    CallMediaInfoVector media;
+
+    /**
+     * Array of provisional media information. This contains the media info
+     * in the provisioning state, that is when the media session is being
+     * created/updated (SDP offer/answer is on progress).
+     */
+    CallMediaInfoVector provMedia;
+    
+    /**
+     * Up-to-date call connected duration (zero when call is not
+     * established)
+     */
+    TimeValue		connectDuration;
+    
+    /**
+     * Total call duration, including set-up time
+     */
+    TimeValue		totalDuration;
+    
+    /**
+     * Flag if remote was SDP offerer
+     */
+    bool		remOfferer;
+    
+    /**
+     * Number of audio streams offered by remote
+     */
+    unsigned		remAudioCount;
+    
+    /**
+     * Number of video streams offered by remote
+     */
+    unsigned		remVideoCount;
+
+public:
+    /**
+     * Convert from pjsip
+     */
+    void fromPj(const pjsua_call_info &pci);
+};
+
+/**
+ * Media stream info.
+ */
+struct StreamInfo
+{
+    /**
+     * Media type of this stream.
+     */
+    pjmedia_type        type;
+
+    /**
+     * Transport protocol (RTP/AVP, etc.)
+     */
+    pjmedia_tp_proto	proto;
+    
+    /**
+     * Media direction.
+     */
+    pjmedia_dir		dir;
+    
+    /**
+     * Remote RTP address
+     */
+    SocketAddress	remoteRtpAddress;
+    
+    /**
+     * Optional remote RTCP address
+     */
+    SocketAddress	remoteRtcpAddress;
+    
+    /**
+     * Outgoing codec payload type.
+     */
+    unsigned		txPt;
+    
+    /**
+     * Incoming codec payload type.
+     */
+    unsigned		rxPt;
+    
+    /**
+     * Codec name.
+     */
+    string              codecName;
+    
+    /**
+     * Codec clock rate.
+     */
+    unsigned            codecClockRate;
+    
+    /**
+     * Optional codec param.
+     */
+    CodecParam          codecParam;
+
+public:
+    /**
+     * Convert from pjsip
+     */
+    void fromPj(const pjsua_stream_info &info);
+};
+
+/**
+ * Media stream statistic.
+ */
+struct StreamStat
+{
+    /**
+     * RTCP statistic.
+     */
+    RtcpStat	rtcp;
+    
+    /**
+     * Jitter buffer statistic.
+     */
+    JbufState	jbuf;
+
+public:
+    /**
+     * Convert from pjsip
+     */
+    void fromPj(const pjsua_stream_stat &prm);
+};
+
+/**
+ * This structure contains parameters for Call::onCallState() callback.
+ */
+struct OnCallStateParam
+{
+    /**
+     * Event which causes the call state to change.
+     */
+    SipEvent    e;
+};
+
+/**
+ * This structure contains parameters for Call::onCallTsxState() callback.
+ */
+struct OnCallTsxStateParam
+{
+    /**
+     * Transaction event that caused the state change.
+     */
+    SipEvent    e;
+};
+
+/**
+ * This structure contains parameters for Call::onCallMediaState() callback.
+ */
+struct OnCallMediaStateParam
+{
+};
+
+/**
+ * This structure contains parameters for Call::onCallSdpCreated() callback.
+ */
+struct OnCallSdpCreatedParam
+{
+    /**
+     * The SDP has just been created.
+     */
+    SdpSession sdp;
+
+    /**
+     * The remote SDP, will be empty if local is SDP offerer.
+     */
+    SdpSession remSdp;
+};
+
+/**
+ * This structure contains parameters for Call::onStreamCreated()
+ * callback.
+ */
+struct OnStreamCreatedParam
+{
+    /**
+     * Media stream.
+     */
+    MediaStream stream;
+    
+    /**
+     * Stream index in the media session.
+     */
+    unsigned    streamIdx;
+    
+    /**
+     * On input, it specifies the media port of the stream. Application
+     * may modify this pointer to point to different media port to be
+     * registered to the conference bridge.
+     */
+    MediaPort   pPort;
+};
+
+/**
+ * This structure contains parameters for Call::onStreamDestroyed()
+ * callback.
+ */
+struct OnStreamDestroyedParam
+{
+    /**
+     * Media stream.
+     */
+    MediaStream stream;
+    
+    /**
+     * Stream index in the media session.
+     */
+    unsigned    streamIdx;
+};
+
+/**
+ * This structure contains parameters for Call::onDtmfDigit()
+ * callback.
+ */
+struct OnDtmfDigitParam
+{
+    /**
+     * DTMF ASCII digit.
+     */
+    string      digit;
+};
+
+/**
+ * This structure contains parameters for Call::onCallTransferRequest()
+ * callback.
+ */
+struct OnCallTransferRequestParam
+{
+    /**
+     * The destination where the call will be transfered to.
+     */
+    string              dstUri;
+    
+    /**
+     * Status code to be returned for the call transfer request. On input,
+     * it contains status code 200.
+     */
+    pjsip_status_code   statusCode;
+    
+    /**
+     * The current call setting, application can update this setting
+     * for the call being transfered.
+     */
+    CallSetting         opt;
+};
+
+/**
+ * This structure contains parameters for Call::onCallTransferStatus()
+ * callback.
+ */
+struct OnCallTransferStatusParam
+{
+    /**
+     * Status progress of the transfer request.
+     */
+    pjsip_status_code   statusCode;
+    
+    /**
+     * Status progress reason.
+     */
+    string              reason;
+    
+    /**
+     * If true, no further notification will be reported. The statusCode
+     * specified in this callback is the final status.
+     */
+    bool                finalNotify;
+    
+    /**
+     * Initially will be set to true, application can set this to false
+     * if it no longer wants to receive further notification (for example,
+     * after it hangs up the call).
+     */
+    bool                cont;
+};
+
+/**
+ * This structure contains parameters for Call::onCallReplaceRequest()
+ * callback.
+ */
+struct OnCallReplaceRequestParam
+{
+    /**
+     * The incoming INVITE request to replace the call.
+     */
+    SipRxData           rdata;
+    
+    /**
+     * Status code to be set by application. Application should only
+     * return a final status (200-699)
+     */
+    pjsip_status_code   statusCode;
+    
+    /**
+     * Optional status text to be set by application.
+     */
+    string              reason;
+    
+    /**
+     * The current call setting, application can update this setting for
+     * the call being replaced.
+     */
+    CallSetting         opt;
+};
+
+/**
+ * This structure contains parameters for Call::onCallReplaced() callback.
+ */
+struct OnCallReplacedParam
+{
+    /**
+     * The new call id.
+     */
+    pjsua_call_id       newCallId;
+};
+
+/**
+ * This structure contains parameters for Call::onCallRxOffer() callback.
+ */
+struct OnCallRxOfferParam
+{
+    /**
+     * The new offer received.
+     */
+    SdpSession          offer;
+    
+    /**
+     * Status code to be returned for answering the offer. On input,
+     * it contains status code 200. Currently, valid values are only
+     * 200 and 488.
+     */
+    pjsip_status_code   statusCode;
+    
+    /**
+     * The current call setting, application can update this setting for
+     * answering the offer.
+     */
+    CallSetting         opt;
+};
+
+/**
+ * This structure contains parameters for Call::onCallRedirected() callback.
+ */
+struct OnCallRedirectedParam
+{
+    /**
+     * The current target to be tried.
+     */
+    string          targetUri;
+    
+    /**
+     * The event that caused this callback to be called.
+     * This could be the receipt of 3xx response, or 4xx/5xx response
+     * received for the INVITE sent to subsequent targets, or empty
+     * (e.type == PJSIP_EVENT_UNKNOWN)
+     * if this callback is called from within Call::processRedirect()
+     * context.
+     */
+    SipEvent        e;
+};
+
+/**
+ * This structure contains parameters for Call::onCallMediaEvent() callback.
+ */
+struct OnCallMediaEventParam
+{
+    /**
+     * The media stream index.
+     */
+    unsigned        medIdx;
+    
+    /**
+     * The media event.
+     */
+    MediaEvent      ev;
+};
+
+/**
+ * This structure contains parameters for Call::onCallMediaTransportState()
+ * callback.
+ */
+struct OnCallMediaTransportStateParam
+{
+    /**
+     * The media index.
+     */
+    unsigned        medIdx;
+    
+    /**
+     * The media transport state
+     */
+    pjsua_med_tp_st state;
+    
+    /**
+     * The last error code related to the media transport state.
+     */
+    pj_status_t     status;
+    
+    /**
+     * Optional SIP error code.
+     */
+    int             sipErrorCode;
+};
+
+/**
+ * This structure contains parameters for Call::onCreateMediaTransport()
+ * callback.
+ */
+struct OnCreateMediaTransportParam
+{
+    /**
+     * The media index in the SDP for which this media transport will be used.
+     */
+    unsigned        mediaIdx;
+    
+    /**
+     * The media transport which otherwise will be used by the call has this
+     * callback not been implemented. Application can change this to its own
+     * instance of media transport to be used by the call.
+     */
+    MediaTransport  mediaTp;
+    
+    /**
+     * Bitmask from pjsua_create_media_transport_flag.
+     */
+    unsigned        flags;
+};
+
+/**
+ * @}  // PJSUA2_Call_Data_Structure
+ */
+
+/**
+ * @addtogroup PJSUA2_CALL
+ * @{
+ */
+
+/**
+ * This structure contains parameters for Call::answer(), Call::hangup(),
+ * Call::reinvite(), Call::update(), Call::xfer(), Call::xferReplaces(),
+ * Call::setHold().
+ */
+struct CallOpParam
+{
+    /**
+     * The call setting.
+     */
+    CallSetting         opt;
+    
+    /**
+     * Status code.
+     */
+    pjsip_status_code   statusCode;
+    
+    /**
+     * Reason phrase.
+     */
+    string              reason;
+    
+    /**
+     * Options.
+     */
+    unsigned            options;
+    
+    /**
+     * List of headers etc to be added to outgoing response message.
+     * Note that this message data will be persistent in all next
+     * answers/responses for this INVITE request.
+     */
+    SipTxOption         txOption;
+    
+public:
+    /**
+     * Default constructor initializes with zero/empty values.
+     * Setting useDefaultCallSetting to true will initialize opt with default
+     * call setting values.
+     */
+    CallOpParam(bool useDefaultCallSetting = false);
+};
+
+/**
+ * This structure contains parameters for Call::sendRequest()
+ */
+struct CallSendRequestParam
+{
+    /**
+     * SIP method of the request.
+     */
+    string       method;
+    
+    /**
+     * Message body and/or list of headers etc to be included in
+     * outgoing request.
+     */
+    SipTxOption  txOption;
+    
+public:
+    /**
+     * Default constructor initializes with zero/empty values.
+     */
+    CallSendRequestParam();
+};
+
+/**
+ * This structure contains parameters for Call::vidSetStream()
+ */
+struct CallVidSetStreamParam
+{
+    /**
+     * Specify the media stream index. This can be set to -1 to denote
+     * the default video stream in the call, which is the first active
+     * video stream or any first video stream if none is active.
+     *
+     * This field is valid for all video stream operations, except
+     * PJSUA_CALL_VID_STRM_ADD.
+     *
+     * Default: -1 (first active video stream, or any first video stream
+     *              if none is active)
+     */
+    int                     medIdx;
+    
+    /**
+     * Specify the media stream direction.
+     *
+     * This field is valid for the following video stream operations:
+     * PJSUA_CALL_VID_STRM_ADD and PJSUA_CALL_VID_STRM_CHANGE_DIR.
+     *
+     * Default: PJMEDIA_DIR_ENCODING_DECODING
+     */
+    pjmedia_dir             dir;
+    
+    /**
+     * Specify the video capture device ID. This can be set to
+     * PJMEDIA_VID_DEFAULT_CAPTURE_DEV to specify the default capture
+     * device as configured in the account.
+     *
+     * This field is valid for the following video stream operations:
+     * PJSUA_CALL_VID_STRM_ADD and PJSUA_CALL_VID_STRM_CHANGE_CAP_DEV.
+     *
+     * Default: PJMEDIA_VID_DEFAULT_CAPTURE_DEV.
+     */
+    pjmedia_vid_dev_index   capDev;
+    
+public:
+    /**
+     * Default constructor
+     */
+    CallVidSetStreamParam();
+};
+
+/**
+ * Call.
+ */
+class Call
+{
+public:
+    /**
+     * Constructor.
+     */
+    Call(Account& acc, int call_id = PJSUA_INVALID_ID);
+
+    /**
+     * Destructor.
+     */
+    virtual ~Call();
+
+    /**
+     * Obtain detail information about this call.
+     *
+     * @return              Call info.
+     */
+    CallInfo getInfo() const throw(Error);
+    
+    /**
+     * Check if this call has active INVITE session and the INVITE
+     * session has not been disconnected.
+     *
+     * @return              True if call is active.
+     */
+    bool isActive() const;
+
+    /**
+     * Get PJSUA-LIB call ID or index associated with this call.
+     *
+     * @return              Integer greater than or equal to zero.
+     */
+    int getId() const;
+    
+    /**
+     * Get the Call class for the specified call Id.
+     *
+     * @param call_id       The call ID to lookup
+     *
+     * @return              The Call instance or NULL if not found.
+     */
+    static Call *lookup(int call_id);
+
+    /**
+     * Check if call has an active media session.
+     *
+     * @return              True if yes.
+     */
+    bool hasMedia() const;
+    
+    /**
+     * Get media for the specified media index.
+     *
+     * @param med_idx       Media index.
+     *
+     * @return              The media or NULL if invalid or inactive.
+     */
+    Media *getMedia(unsigned med_idx) const;
+
+    /**
+     * Check if remote peer support the specified capability.
+     *
+     * @param htype         The header type (pjsip_hdr_e) to be checked, which
+     *                      value may be:
+     *                      - PJSIP_H_ACCEPT
+     *                      - PJSIP_H_ALLOW
+     *                      - PJSIP_H_SUPPORTED
+     * @param hname         If htype specifies PJSIP_H_OTHER, then the header
+     *                      name must be supplied in this argument. Otherwise
+     *                      the value must be set to empty string ("").
+     * @param token         The capability token to check. For example, if \a
+     *                      htype is PJSIP_H_ALLOW, then \a token specifies the
+     *                      method names; if \a htype is PJSIP_H_SUPPORTED, then
+     *                      \a token specifies the extension names such as
+     *                      "100rel".
+     *
+     * @return              PJSIP_DIALOG_CAP_SUPPORTED if the specified
+     *                      capability is explicitly supported, see
+     *                      pjsip_dialog_cap_status for more info.
+     */
+    pjsip_dialog_cap_status remoteHasCap(int htype,
+                                         const string &hname,
+                                         const string &token) const;
+    
+    /**
+     * Attach application specific data to the call. Application can then
+     * inspect this data by calling getUserData().
+     *
+     * @param user_data     Arbitrary data to be attached to the call.
+     */
+    void setUserData(Token user_data);
+    
+    /**
+     * Get user data attached to the call, which has been previously set with
+     * setUserData().
+     *
+     * @return              The user data.
+     */
+    Token getUserData() const;
+    
+    /**
+     * Get the NAT type of remote's endpoint. This is a proprietary feature
+     * of PJSUA-LIB which sends its NAT type in the SDP when \a natTypeInSdp
+     * is set in UaConfig.
+     *
+     * This function can only be called after SDP has been received from remote,
+     * which means for incoming call, this function can be called as soon as
+     * call is received as long as incoming call contains SDP, and for outgoing
+     * call, this function can be called only after SDP is received (normally in
+     * 200/OK response to INVITE). As a general case, application should call
+     * this function after or in \a onCallMediaState() callback.
+     *
+     * @return              The NAT type.
+     *
+     * @see Endpoint::natGetType(), natTypeInSdp
+     */
+    pj_stun_nat_type getRemNatType() throw(Error);
+
+    /**
+     * Make outgoing call to the specified URI.
+     *
+     * @param dst_uri       URI to be put in the To header (normally is the same
+     *                      as the target URI).
+     * @param prm.opt       Optional call setting.
+     * @param prm.txOption  Optional headers etc to be added to outgoing INVITE
+     *                      request.
+     */
+    void makeCall(const string &dst_uri, const CallOpParam &prm) throw(Error);
+
+    /**
+     * Send response to incoming INVITE request with call setting param.
+     * Depending on the status code specified as parameter, this function may
+     * send provisional response, establish the call, or terminate the call.
+     * Notes about call setting:
+     *  - if call setting is changed in the subsequent call to this function,
+     *    only the first call setting supplied will applied. So normally
+     *    application will not supply call setting before getting confirmation
+     *    from the user.
+     *  - if no call setting is supplied when SDP has to be sent, i.e: answer
+     *    with status code 183 or 2xx, the default call setting will be used,
+     *    check CallSetting for its default values.
+     *
+     * @param prm.opt       Optional call setting.
+     * @param prm.statusCode   Status code, (100-699).
+     * @param prm.reason    Optional reason phrase. If empty, default text
+     *                      will be used.
+     * @param prm.txOption  Optional list of headers etc to be added to outgoing
+     *                      response message. Note that this message data will
+     *                      be persistent in all next answers/responses for this
+     *                      INVITE request.
+     */
+    void answer(const CallOpParam &prm) throw(Error);
+    
+    /**
+     * Hangup call by using method that is appropriate according to the
+     * call state. This function is different than answering the call with
+     * 3xx-6xx response (with answer()), in that this function
+     * will hangup the call regardless of the state and role of the call,
+     * while answer() only works with incoming calls on EARLY
+     * state.
+     *
+     * @param prm.statusCode
+     *                      Optional status code to be sent when we're rejecting
+     *                      incoming call. If the value is zero, "603/Decline"
+     *                      will be sent.
+     * @param prm.reason    Optional reason phrase to be sent when we're
+     *                      rejecting incoming call. If empty, default text
+     *                      will be used.
+     * @param prm.txOption  Optional list of headers etc to be added to outgoing
+     *                      request/response message.
+     */
+    void hangup(const CallOpParam &prm) throw(Error);
+    
+    /**
+     * Put the specified call on hold. This will send re-INVITE with the
+     * appropriate SDP to inform remote that the call is being put on hold.
+     * The final status of the request itself will be reported on the
+     * \a onCallMediaState() callback, which inform the application that
+     * the media state of the call has changed.
+     *
+     * @param prm.options   Bitmask of pjsua_call_flag constants. Currently,
+     *                      only the flag PJSUA_CALL_UPDATE_CONTACT can be used.
+     * @param prm.txOption  Optional message components to be sent with
+     *                      the request.
+     */
+    void setHold(const CallOpParam &prm) throw(Error);
+    
+    /**
+     * Send re-INVITE to release hold.
+     * The final status of the request itself will be reported on the
+     * \a onCallMediaState() callback, which inform the application that
+     * the media state of the call has changed.
+     *
+     * @param prm.opt       Optional call setting, if empty, the current call
+     *                      setting will remain unchanged.
+     * @param prm.txOption  Optional message components to be sent with
+     *                      the request.
+     */
+    void reinvite(const CallOpParam &prm) throw(Error);
+    
+    /**
+     * Send UPDATE request.
+     *
+     * @param prm.opt       Optional call setting, if empty, the current call
+     *                      setting will remain unchanged.
+     * @param prm.txOption  Optional message components to be sent with
+     *                      the request.
+     */
+    void update(const CallOpParam &prm) throw(Error);
+    
+    /**
+     * Initiate call transfer to the specified address. This function will send
+     * REFER request to instruct remote call party to initiate a new INVITE
+     * session to the specified destination/target.
+     *
+     * If application is interested to monitor the successfulness and
+     * the progress of the transfer request, it can implement
+     * \a onCallTransferStatus() callback which will report the progress
+     * of the call transfer request.
+     *
+     * @param dest          URI of new target to be contacted. The URI may be
+     *                      in name address or addr-spec format.
+     * @param prm.txOption  Optional message components to be sent with
+     *                      the request.
+     */
+    void xfer(const string &dest, const CallOpParam &prm) throw(Error);
+
+    /**
+     * Initiate attended call transfer. This function will send REFER request
+     * to instruct remote call party to initiate new INVITE session to the URL
+     * of \a destCall. The party at \a dest_call then should "replace"
+     * the call with us with the new call from the REFER recipient.
+     *
+     * @param dest_call     The call to be replaced.
+     * @param prm.options   Application may specify
+     *                      PJSUA_XFER_NO_REQUIRE_REPLACES to suppress the 
+     *                      inclusion of "Require: replaces" in
+     *                      the outgoing INVITE request created by the REFER
+     *                      request.
+     * @param prm.txOption  Optional message components to be sent with
+     *                      the request.
+     */
+    void xferReplaces(const Call& dest_call,
+                      const CallOpParam &prm) throw(Error);
+    
+    /**
+     * Accept or reject redirection response. Application MUST call this
+     * function after it signaled PJSIP_REDIRECT_PENDING in the 
+     * \a onCallRedirected() callback,
+     * to notify the call whether to accept or reject the redirection
+     * to the current target. Application can use the combination of
+     * PJSIP_REDIRECT_PENDING command in \a onCallRedirected() callback and
+     * this function to ask for user permission before redirecting the call.
+     *
+     * Note that if the application chooses to reject or stop redirection (by
+     * using PJSIP_REDIRECT_REJECT or PJSIP_REDIRECT_STOP respectively), the
+     * call disconnection callback will be called before this function returns.
+     * And if the application rejects the target, the \a onCallRedirected()
+     * callback may also be called before this function returns if there is
+     * another target to try.
+     *
+     * @param cmd           Redirection operation to be applied to the current
+     *                      target. The semantic of this argument is similar
+     *                      to the description in the \a onCallRedirected()
+     *                      callback, except that the PJSIP_REDIRECT_PENDING is
+     *                      not accepted here.
+     */
+    void processRedirect(pjsip_redirect_op cmd) throw(Error);
+
+    /**
+     * Send DTMF digits to remote using RFC 2833 payload formats.
+     *
+     * @param digits        DTMF string digits to be sent.
+     */
+    void dialDtmf(const string &digits) throw(Error);
+    
+    /**
+     * Send instant messaging inside INVITE session.
+     *
+     * @param prm.contentType
+     *                      MIME type.
+     * @param prm.content   The message content.
+     * @param prm.txOption  Optional list of headers etc to be included in
+     *                      outgoing request. The body descriptor in the
+     *                      txOption is ignored.
+     * @param prm.userData  Optional user data, which will be given back when
+     *                      the IM callback is called.
+     */
+    void sendInstantMessage(const SendInstantMessageParam& prm) throw(Error);
+    
+    /**
+     * Send IM typing indication inside INVITE session.
+     *
+     * @param prm.isTyping  True to indicate to remote that local person is
+     *                      currently typing an IM.
+     * @param prm.txOption  Optional list of headers etc to be included in
+     *                      outgoing request.
+     */
+    void sendTypingIndication(const SendTypingIndicationParam &prm)
+         throw(Error);
+    
+    /**
+     * Send arbitrary request with the call. This is useful for example to send
+     * INFO request. Note that application should not use this function to send
+     * requests which would change the invite session's state, such as
+     * re-INVITE, UPDATE, PRACK, and BYE.
+     *
+     * @param prm.method    SIP method of the request.
+     * @param prm.txOption  Optional message body and/or list of headers to be
+     *                      included in outgoing request.
+     */
+    void sendRequest(const CallSendRequestParam &prm) throw(Error);
+    
+    /**
+     * Dump call and media statistics to string.
+     *
+     * @param with_media    True to include media information too.
+     * @param indent        Spaces for left indentation.
+     *
+     * @return              Call dump and media statistics string.
+     */
+    string dump(bool with_media, const string indent) throw(Error);
+    
+    /**
+     * Get the media stream index of the default video stream in the call.
+     * Typically this will just retrieve the stream index of the first
+     * activated video stream in the call. If none is active, it will return
+     * the first inactive video stream.
+     *
+     * @return              The media stream index or -1 if no video stream
+     *                      is present in the call.
+     */
+    int vidGetStreamIdx() const;
+    
+    /**
+     * Determine if video stream for the specified call is currently running
+     * (i.e. has been created, started, and not being paused) for the specified
+     * direction.
+     *
+     * @param med_idx       Media stream index, or -1 to specify default video
+     *                      media.
+     * @param dir           The direction to be checked.
+     *
+     * @return              True if stream is currently running for the
+     *                      specified direction.
+     */
+    bool vidStreamIsRunning(int med_idx, pjmedia_dir dir) const;
+    
+    /**
+     * Add, remove, modify, and/or manipulate video media stream for the
+     * specified call. This may trigger a re-INVITE or UPDATE to be sent
+     * for the call.
+     *
+     * @param op            The video stream operation to be performed,
+     *                      possible values are pjsua_call_vid_strm_op.
+     * @param param         The parameters for the video stream operation
+     *                      (see CallVidSetStreamParam).
+     */
+    void vidSetStream(pjsua_call_vid_strm_op op,
+                      const CallVidSetStreamParam &param) throw(Error);
+
+    /**
+     * Get media stream info for the specified media index.
+     *
+     * @param med_idx       Media stream index.
+     *
+     * @return              The stream info.
+     */
+    StreamInfo getStreamInfo(unsigned med_idx) const throw(Error);
+    
+    /**
+     * Get media stream statistic for the specified media index.
+     *
+     * @param med_idx       Media stream index.
+     *
+     * @return              The stream statistic.
+     */
+    StreamStat getStreamStat(unsigned med_idx) const throw(Error);
+    
+    /**
+     * Get media transport info for the specified media index.
+     *
+     * @param med_idx       Media stream index.
+     *
+     * @return              The transport info.
+     */
+    MediaTransportInfo getMedTransportInfo(unsigned med_idx) const throw(Error);
+
+    /**
+     * Internal function (callled by Endpoint( to process update to call
+     * medias when call media state changes.
+     */
+    void processMediaUpdate(OnCallMediaStateParam &prm);
+
+    /**
+     * Internal function (called by Endpoint) to process call state change.
+     */
+    void processStateChange(OnCallStateParam &prm);
+    
+public:
+    /*
+     * Callbacks
+     */
+    /**
+     * Notify application when call state has changed.
+     * Application may then query the call info to get the
+     * detail call states by calling getInfo() function.
+     *
+     * @param prm	Callback parameter.
+     */
+    virtual void onCallState(OnCallStateParam &prm)
+    { PJ_UNUSED_ARG(prm); }
+    
+    /**
+     * This is a general notification callback which is called whenever
+     * a transaction within the call has changed state. Application can
+     * implement this callback for example to monitor the state of
+     * outgoing requests, or to answer unhandled incoming requests
+     * (such as INFO) with a final response.
+     *
+     * @param prm	Callback parameter.
+     */
+    virtual void onCallTsxState(OnCallTsxStateParam &prm)
+    { PJ_UNUSED_ARG(prm); }
+    
+    /**
+     * Notify application when media state in the call has changed.
+     * 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.
+     *
+     * @param prm	Callback parameter.
+     */
+    virtual void onCallMediaState(OnCallMediaStateParam &prm)
+    { PJ_UNUSED_ARG(prm); }
+    
+    /**
+     * Notify application when a call has just created a local SDP (for
+     * initial or subsequent SDP offer/answer). Application can implement
+     * this callback to modify the SDP, before it is being sent and/or
+     * negotiated with remote SDP, for example to apply per account/call
+     * basis codecs priority or to add custom/proprietary SDP attributes.
+     *
+     * @param prm	Callback parameter.
+     */
+    virtual void onCallSdpCreated(OnCallSdpCreatedParam &prm)
+    { PJ_UNUSED_ARG(prm); }
+    
+    /**
+     * Notify application when media session is created and before it is
+     * registered to the conference bridge. Application may return different
+     * media port if it has added media processing port to the stream. This
+     * media port then will be added to the conference bridge instead.
+     *
+     * @param prm	Callback parameter.
+     */
+    virtual void onStreamCreated(OnStreamCreatedParam &prm)
+    { PJ_UNUSED_ARG(prm); }
+    
+    /**
+     * Notify application when media session has been unregistered from the
+     * conference bridge and about to be destroyed.
+     *
+     * @param prm	Callback parameter.
+     */
+    virtual void onStreamDestroyed(OnStreamDestroyedParam &prm)
+    { PJ_UNUSED_ARG(prm); }
+    
+    /**
+     * Notify application upon incoming DTMF digits.
+     *
+     * @param prm	Callback parameter.
+     */
+    virtual void onDtmfDigit(OnDtmfDigitParam &prm)
+    { PJ_UNUSED_ARG(prm); }
+    
+    /**
+     * Notify application on call being transfered (i.e. REFER is received).
+     * Application can decide to accept/reject transfer request
+     * by setting the code (default is 202). When this callback
+     * is not implemented, the default behavior is to accept the
+     * transfer.
+     *
+     * @param prm	Callback parameter.
+     */
+    virtual void onCallTransferRequest(OnCallTransferRequestParam &prm)
+    { PJ_UNUSED_ARG(prm); }
+    
+    /**
+     * Notify application of the status of previously sent call
+     * transfer request. Application can monitor the status of the
+     * call transfer request, for example to decide whether to
+     * terminate existing call.
+     *
+     * @param prm	Callback parameter.
+     */
+    virtual void onCallTransferStatus(OnCallTransferStatusParam &prm)
+    { PJ_UNUSED_ARG(prm); }
+    
+    /**
+     * Notify application about incoming INVITE with Replaces header.
+     * Application may reject the request by setting non-2xx code.
+     *
+     * @param prm	Callback parameter.
+     */
+    virtual void onCallReplaceRequest(OnCallReplaceRequestParam &prm)
+    { PJ_UNUSED_ARG(prm); }
+    
+    /**
+     * Notify application that an existing call has been replaced with
+     * a new call. This happens when PJSUA-API receives incoming INVITE
+     * request with Replaces header.
+     *
+     * After this callback is called, normally PJSUA-API will disconnect
+     * this call and establish a new call \a newCallId.
+     *
+     * @param prm	Callback parameter.
+     */
+    virtual void onCallReplaced(OnCallReplacedParam &prm)
+    { PJ_UNUSED_ARG(prm); }
+    
+    /**
+     * Notify application when call has received new offer from remote
+     * (i.e. re-INVITE/UPDATE with SDP is received). Application can
+     * decide to accept/reject the offer by setting the code (default
+     * is 200). If the offer is accepted, application can update the
+     * call setting to be applied in the answer. When this callback is
+     * not implemented, the default behavior is to accept the offer using
+     * current call setting.
+     *
+     * @param prm	Callback parameter.
+     */
+    virtual void onCallRxOffer(OnCallRxOfferParam &prm)
+    { PJ_UNUSED_ARG(prm); }
+    
+    /**
+     * Notify application on incoming MESSAGE request.
+     *
+     * @param prm	Callback parameter.
+     */
+    virtual void onInstantMessage(OnInstantMessageParam &prm)
+    { PJ_UNUSED_ARG(prm); }
+    
+    /**
+     * Notify application about the delivery status of outgoing MESSAGE
+     * request.
+     *
+     * @param prm	Callback parameter.
+     */
+    virtual void onInstantMessageStatus(OnInstantMessageStatusParam &prm)
+    { PJ_UNUSED_ARG(prm); }
+    
+    /**
+     * Notify application about typing indication.
+     *
+     * @param prm	Callback parameter.
+     */
+    virtual void onTypingIndication(OnTypingIndicationParam &prm)
+    { PJ_UNUSED_ARG(prm); }
+    
+    /**
+     * This callback is called when the call is about to resend the
+     * INVITE request to the specified target, following the previously
+     * received redirection response.
+     *
+     * Application may accept the redirection to the specified target,
+     * reject this target only and make the session continue to try the next
+     * target in the list if such target exists, stop the whole
+     * redirection process altogether and cause the session to be
+     * disconnected, or defer the decision to ask for user confirmation.
+     *
+     * This callback is optional,
+     * the default behavior is to NOT follow the redirection response.
+     *
+     * @param prm	Callback parameter.
+     *
+     * @return		Action to be performed for the target. Set this
+     *			parameter to one of the value below:
+     *			- PJSIP_REDIRECT_ACCEPT: immediately accept the
+     *			  redirection. When set, the call will immediately
+     *			  resend INVITE request to the target.
+     *			- PJSIP_REDIRECT_ACCEPT_REPLACE: immediately accept
+     *			  the redirection and replace the To header with the
+     *			  current target. When set, the call will immediately
+     *			  resend INVITE request to the target.
+     *			- PJSIP_REDIRECT_REJECT: immediately reject this
+     *			  target. The call will continue retrying with
+     *			  next target if present, or disconnect the call
+     *			  if there is no more target to try.
+     *			- PJSIP_REDIRECT_STOP: stop the whole redirection
+     *			  process and immediately disconnect the call. The
+     *			  onCallState() callback will be called with
+     *			  PJSIP_INV_STATE_DISCONNECTED state immediately
+     *			  after this callback returns.
+     *			- PJSIP_REDIRECT_PENDING: set to this value if
+     *			  no decision can be made immediately (for example
+     *			  to request confirmation from user). Application
+     *			  then MUST call processRedirect()
+     *			  to either accept or reject the redirection upon
+     *			  getting user decision.
+     */
+    virtual pjsip_redirect_op onCallRedirected(OnCallRedirectedParam &prm)
+    {
+	PJ_UNUSED_ARG(prm);
+        return PJSIP_REDIRECT_STOP;
+    }
+    
+    /**
+     * This callback is called when media transport state is changed.
+     *
+     * @param prm	Callback parameter.
+     */
+    virtual void onCallMediaTransportState(OnCallMediaTransportStateParam &prm)
+    { PJ_UNUSED_ARG(prm); }
+    
+    /**
+     * Notification about media events such as video notifications. This
+     * callback will most likely be called from media threads, thus
+     * application must not perform heavy processing in this callback.
+     * Especially, application must not destroy the call or media in this
+     * callback. If application needs to perform more complex tasks to
+     * handle the event, it should post the task to another thread.
+     *
+     * @param prm	Callback parameter.
+     */
+    virtual void onCallMediaEvent(OnCallMediaEventParam &prm)
+    { PJ_UNUSED_ARG(prm); }
+    
+    /**
+     * This callback can be used by application to implement custom media
+     * transport adapter for the call, or to replace the media transport
+     * with something completely new altogether.
+     *
+     * This callback is called when a new call is created. The library has
+     * created a media transport for the call, and it is provided as the
+     * \a mediaTp argument of this callback. The callback may change it
+     * with the instance of media transport to be used by the call.
+     *
+     * @param prm	Callback parameter.
+     */
+    virtual void
+    onCreateMediaTransport(OnCreateMediaTransportParam &prm)
+    { PJ_UNUSED_ARG(prm); }
+
+private:
+    Account             &acc;
+    pjsua_call_id 	 id;
+    Token                userData;
+    std::vector<Media *> medias;
+};
+
+/**
+ * @}  // PJSUA2_CALL
+ */
+
+} // namespace pj
+
+#endif	/* __PJSUA2_CALL_HPP__ */
+
Index: /pjproject/trunk/pjsip/include/pjsua2/config.hpp
===================================================================
--- /pjproject/trunk/pjsip/include/pjsua2/config.hpp	(revision 4704)
+++ /pjproject/trunk/pjsip/include/pjsua2/config.hpp	(revision 4704)
@@ -0,0 +1,47 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2013 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 __PJSUA2_CONFIG_HPP__
+#define __PJSUA2_CONFIG_HPP__
+
+/**
+ * @file pjsua2/config.hpp
+ * @brief PJSUA2 Base Agent Operation
+ */
+#include <pjsua-lib/pjsua.h>
+
+/**
+ * @defgroup PJSUA2_CFG_Compile Compile time settings
+ * @ingroup PJSUA2_DS
+ * @{
+ */
+
+/**
+ * Specify if the Error exception info should contain operation and source
+ * file information.
+ */
+#ifndef PJSUA2_ERROR_HAS_EXTRA_INFO
+#   define PJSUA2_ERROR_HAS_EXTRA_INFO		1
+#endif
+
+
+/**
+ * @}  PJSUA2_CFG
+ */
+
+#endif	/* __PJSUA2_CONFIG_HPP__ */
Index: /pjproject/trunk/pjsip/include/pjsua2/doxygen.hpp
===================================================================
--- /pjproject/trunk/pjsip/include/pjsua2/doxygen.hpp	(revision 4704)
+++ /pjproject/trunk/pjsip/include/pjsua2/doxygen.hpp	(revision 4704)
@@ -0,0 +1,52 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2008-2012 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 __PJSUA2_DOXYGEN_HPP__
+#define __PJSUA2_DOXYGEN_HPP__
+
+/**
+ * @file pjsua2/doxygen.hpp
+ * @brief PJSUA2 Doxygen documentation
+ */
+
+/**
+@mainpage pjsua2 API
+
+This documentation contains the API Reference for pjsua2. Please
+go to <b>Modules</b> section above for navigation.
+
+For more info regarding pjsua2, including some tutorials, please
+see The PJSIP Book.
+*/
+
+
+/**
+ * @defgroup PJSUA2_Ref pjsua2 API Reference
+ */
+
+/**
+ * @defgroup PJSUA2_DS Data Structure
+ * @ingroup PJSUA2_Ref
+ */
+
+
+/**
+ * @}  PJSUA2_CFG
+ */
+
+#endif	/* __PJSUA2_DOXYGEN_HPP__ */
Index: /pjproject/trunk/pjsip/include/pjsua2/endpoint.hpp
===================================================================
--- /pjproject/trunk/pjsip/include/pjsua2/endpoint.hpp	(revision 4704)
+++ /pjproject/trunk/pjsip/include/pjsua2/endpoint.hpp	(revision 4704)
@@ -0,0 +1,1322 @@
+/* $Id$ */
+/* 
+ * Copyright (C) 2013 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 __PJSUA2_UA_HPP__
+#define __PJSUA2_UA_HPP__
+
+/**
+ * @file pjsua2/endpoint.hpp
+ * @brief PJSUA2 Base Agent Operation
+ */
+#include <pjsua2/persistent.hpp>
+#include <pjsua2/media.hpp>
+#include <pjsua2/siptypes.hpp>
+#include <list>
+
+/** PJSUA2 API is inside pj namespace */
+namespace pj
+{
+
+/**
+ * @defgroup PJSUA2_UA Endpoint
+ * @ingroup PJSUA2_Ref
+ * @{
+ */
+
+using std::string;
+using std::vector;
+
+
+//////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Argument to Endpoint::onNatDetectionComplete() callback.
+ */
+struct OnNatDetectionCompleteParam
+{
+    /**
+     * Status of the detection process. If this value is not PJ_SUCCESS,
+     * the detection has failed and \a nat_type field will contain
+     * PJ_STUN_NAT_TYPE_UNKNOWN.
+     */
+    pj_status_t		status;
+
+    /**
+     * The text describing the status, if the status is not PJ_SUCCESS.
+     */
+    string		reason;
+
+    /**
+     * This contains the NAT type as detected by the detection procedure.
+     * This value is only valid when the \a status is PJ_SUCCESS.
+     */
+    pj_stun_nat_type	natType;
+
+    /**
+     * Text describing that NAT type.
+     */
+    string		natTypeName;
+
+};
+
+/**
+ * Argument to Endpoint::onNatCheckStunServersComplete() callback.
+ */
+struct OnNatCheckStunServersCompleteParam
+{
+    /**
+     * Arbitrary user data that was passed to Endpoint::natCheckStunServers()
+     * function.
+     */
+    Token		userData;
+
+    /**
+     * This will contain PJ_SUCCESS if at least one usable STUN server
+     * is found, otherwise it will contain the last error code during
+     * the operation.
+     */
+    pj_status_t		status;
+
+    /**
+     * The server name that yields successful result. This will only
+     * contain value if status is successful.
+     */
+    string		name;
+
+    /**
+     * The server IP address and port in "IP:port" format. This will only
+     * contain value if status is successful.
+     */
+    SocketAddress	addr;
+};
+
+/**
+ * Parameter of Endpoint::onTimer() callback.
+ */
+struct OnTimerParam
+{
+    /**
+     * Arbitrary user data that was passed to Endpoint::utilTimerSchedule()
+     * function.
+     */
+    Token		userData;
+
+    /**
+     * The interval of this timer, in miliseconds.
+     */
+    unsigned		msecDelay;
+};
+
+/**
+ * Parameter of Endpoint::onTransportState() callback.
+ */
+struct OnTransportStateParam
+{
+    /**
+     * The transport handle.
+     */
+    TransportHandle	hnd;
+
+    /**
+     * Transport current state.
+     */
+    pjsip_transport_state state;
+
+    /**
+     * The last error code related to the transport state.
+     */
+    pj_status_t		lastError;
+};
+
+/**
+ * Parameter of Endpoint::onSelectAccount() callback.
+ */
+struct OnSelectAccountParam
+{
+    /**
+     * The incoming request.
+     */
+    SipRxData		rdata;
+
+    /**
+     * The account index to be used to handle the request.
+     * Upon entry, this will be filled by the account index
+     * chosen by the library. Application may change it to
+     * another value to use another account.
+     */
+    int			accountIndex;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+/**
+ * SIP User Agent related settings.
+ */
+struct UaConfig : public PersistentObject
+{
+    /**
+     * Maximum calls to support (default: 4). The value specified here
+     * must be smaller than the compile time maximum settings
+     * PJSUA_MAX_CALLS, which by default is 32. To increase this
+     * limit, the library must be recompiled with new PJSUA_MAX_CALLS
+     * value.
+     */
+    unsigned		maxCalls;
+
+    /**
+     * Number of worker threads. Normally application will want to have at
+     * least one worker thread, unless when it wants to poll the library
+     * periodically, which in this case the worker thread can be set to
+     * zero.
+     */
+    unsigned		threadCnt;
+
+    /**
+     * When this flag is non-zero, all callbacks that come from thread
+     * other than main thread will be posted to the main thread and
+     * to be executed by Endpoint::libHandleEvents() function. This
+     * includes the logging callback. Note that this will only work if
+     * threadCnt is set to zero and Endpoint::libHandleEvents() is
+     * performed by main thread. By default, the main thread is set
+     * from the thread that invoke Endpoint::libCreate()
+     *
+     * Default: false
+     */
+    bool		mainThreadOnly;
+
+    /**
+     * Array of nameservers to be used by the SIP resolver subsystem.
+     * The order of the name server specifies the priority (first name
+     * server will be used first, unless it is not reachable).
+     */
+    StringVector	nameserver;
+
+    /**
+     * Optional user agent string (default empty). If it's empty, no
+     * User-Agent header will be sent with outgoing requests.
+     */
+    string		userAgent;
+
+    /**
+     * Array of STUN servers to try. The library will try to resolve and
+     * contact each of the STUN server entry until it finds one that is
+     * usable. Each entry may be a domain name, host name, IP address, and
+     * it may contain an optional port number. For example:
+     *	- "pjsip.org" (domain name)
+     *	- "sip.pjsip.org" (host name)
+     *	- "pjsip.org:33478" (domain name and a non-standard port number)
+     *	- "10.0.0.1:3478" (IP address and port number)
+     *
+     * When nameserver is configured in the \a pjsua_config.nameserver field,
+     * if entry is not an IP address, it will be resolved with DNS SRV
+     * resolution first, and it will fallback to use DNS A resolution if this
+     * fails. Port number may be specified even if the entry is a domain name,
+     * in case the DNS SRV resolution should fallback to a non-standard port.
+     *
+     * When nameserver is not configured, entries will be resolved with
+     * pj_gethostbyname() if it's not an IP address. Port number may be
+     * specified if the server is not listening in standard STUN port.
+     */
+    StringVector	stunServer;
+
+    /**
+     * 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.
+     *
+     * Default: TRUE
+     */
+    bool		stunIgnoreFailure;
+
+    /**
+     * Support for adding and parsing NAT type in the SDP to assist
+     * troubleshooting. The valid values are:
+     *	- 0: no information will be added in SDP, and parsing is disabled.
+     *	- 1: only the NAT type number is added.
+     *	- 2: add both NAT type number and name.
+     *
+     * Default: 1
+     */
+    int			natTypeInSdp;
+
+    /**
+     * Handle unsolicited NOTIFY requests containing message waiting
+     * indication (MWI) info. Unsolicited MWI is incoming NOTIFY requests
+     * which are not requested by client with SUBSCRIBE request.
+     *
+     * If this is enabled, the library will respond 200/OK to the NOTIFY
+     * request and forward the request to Endpoint::onMwiInfo() callback.
+     *
+     * See also AccountMwiConfig.enabled.
+     *
+     * Default: PJ_TRUE
+     */
+    bool	    	mwiUnsolicitedEnabled;
+
+public:
+    /**
+     * Default constructor to initialize with default values.
+     */
+    UaConfig();
+
+    /**
+     * Construct from pjsua_config.
+     */
+    void fromPj(const pjsua_config &ua_cfg);
+
+    /**
+     * Export to pjsua_config
+     */
+    pjsua_config toPj() const;
+
+    /**
+     * Read this object from a container.
+     *
+     * @param node		Container to write values from.
+     */
+    virtual void readObject(const ContainerNode &node) throw(Error);
+
+    /**
+     * Write this object to a container.
+     *
+     * @param node		Container to write values to.
+     */
+    virtual void writeObject(ContainerNode &node) const throw(Error);
+
+};
+
+
+/**
+ * Data containing log entry to be written by the LogWriter.
+ */
+struct LogEntry
+{
+    /** Log verbosity level of this message */
+    int		level;
+
+    /** The log message */
+    string	msg;
+
+    /** ID of current thread */
+    long	threadId;
+
+    /** The name of the thread that writes this log */
+    string	threadName;
+};
+
+
+/**
+ * Interface for writing log messages. Applications can inherit this class
+ * and supply it in the LogConfig structure to implement custom log
+ * writing facility.
+ */
+class LogWriter
+{
+public:
+    /** Destructor */
+    virtual ~LogWriter() {}
+
+    /** Write a log entry. */
+    virtual void write(const LogEntry &entry) = 0;
+};
+
+
+/**
+ * Logging configuration, which can be (optionally) specified when calling
+ * Lib::init().
+ */
+struct LogConfig : public PersistentObject
+{
+    /** Log incoming and outgoing SIP message? Yes!  */
+    unsigned		msgLogging;
+
+    /** Input verbosity level. Value 5 is reasonable. */
+    unsigned		level;
+
+    /** Verbosity level for console. Value 4 is reasonable. */
+    unsigned		consoleLevel;
+
+    /** Log decoration. */
+    unsigned		decor;
+
+    /** Optional log filename if app wishes the library to write to log file.
+     */
+    string		filename;
+
+    /**
+     * Additional flags to be given to pj_file_open() when opening
+     * the log file. By default, the flag is PJ_O_WRONLY. Application
+     * may set PJ_O_APPEND here so that logs are appended to existing
+     * file instead of overwriting it.
+     *
+     * Default is 0.
+     */
+    unsigned		fileFlags;
+
+    /**
+     * Custom log writer, if required. This instance will be destroyed
+     * by the endpoint when the endpoint is destroyed.
+     */
+    LogWriter		*writer;
+
+public:
+    /** Default constructor initialises with default values */
+    LogConfig();
+
+    /** Construct from pjsua_logging_config */
+    void fromPj(const pjsua_logging_config &lc);
+
+    /** Generate pjsua_logging_config. */
+    pjsua_logging_config toPj() const;
+
+    /**
+     * Read this object from a container.
+     *
+     * @param node		Container to write values from.
+     */
+    virtual void readObject(const ContainerNode &node) throw(Error);
+
+    /**
+     * Write this object to a container.
+     *
+     * @param node		Container to write values to.
+     */
+    virtual void writeObject(ContainerNode &node) const throw(Error);
+};
+
+
+/**
+ * This structure describes media configuration, which will be specified
+ * when calling Lib::init().
+ */
+struct MediaConfig : public PersistentObject
+{
+public:
+    /**
+     * Clock rate to be applied to the conference bridge.
+     * If value is zero, default clock rate will be used
+     * (PJSUA_DEFAULT_CLOCK_RATE, which by default is 16KHz).
+     */
+    unsigned		clockRate;
+
+    /**
+     * Clock rate to be applied when opening the sound device.
+     * If value is zero, conference bridge clock rate will be used.
+     */
+    unsigned		sndClockRate;
+
+    /**
+     * Channel count be applied when opening the sound device and
+     * conference bridge.
+     */
+    unsigned		channelCount;
+
+    /**
+     * Specify audio frame ptime. The value here will affect the
+     * samples per frame of both the sound device and the conference
+     * bridge. Specifying lower ptime will normally reduce the
+     * latency.
+     *
+     * Default value: PJSUA_DEFAULT_AUDIO_FRAME_PTIME
+     */
+    unsigned		audioFramePtime;
+
+    /**
+     * Specify maximum number of media ports to be created in the
+     * conference bridge. Since all media terminate in the bridge
+     * (calls, file player, file recorder, etc), the value must be
+     * large enough to support all of them. However, the larger
+     * the value, the more computations are performed.
+     *
+     * Default value: PJSUA_MAX_CONF_PORTS
+     */
+    unsigned		maxMediaPorts;
+
+    /**
+     * Specify whether the media manager should manage its own
+     * ioqueue for the RTP/RTCP sockets. If yes, ioqueue will be created
+     * and at least one worker thread will be created too. If no,
+     * the RTP/RTCP sockets will share the same ioqueue as SIP sockets,
+     * and no worker thread is needed.
+     *
+     * Normally application would say yes here, unless it wants to
+     * run everything from a single thread.
+     */
+    bool		hasIoqueue;
+
+    /**
+     * Specify the number of worker threads to handle incoming RTP
+     * packets. A value of one is recommended for most applications.
+     */
+    unsigned		threadCnt;
+
+    /**
+     * Media quality, 0-10, according to this table:
+     *   5-10: resampling use large filter,
+     *   3-4:  resampling use small filter,
+     *   1-2:  resampling use linear.
+     * The media quality also sets speex codec quality/complexity to the
+     * number.
+     *
+     * Default: 5 (PJSUA_DEFAULT_CODEC_QUALITY).
+     */
+    unsigned		quality;
+
+    /**
+     * Specify default codec ptime.
+     *
+     * Default: 0 (codec specific)
+     */
+    unsigned		ptime;
+
+    /**
+     * Disable VAD?
+     *
+     * Default: 0 (no (meaning VAD is enabled))
+     */
+    bool		noVad;
+
+    /**
+     * iLBC mode (20 or 30).
+     *
+     * Default: 30 (PJSUA_DEFAULT_ILBC_MODE)
+     */
+    unsigned		ilbcMode;
+
+    /**
+     * Percentage of RTP packet to drop in TX direction
+     * (to simulate packet lost).
+     *
+     * Default: 0
+     */
+    unsigned		txDropPct;
+
+    /**
+     * Percentage of RTP packet to drop in RX direction
+     * (to simulate packet lost).
+     *
+     * Default: 0
+     */
+    unsigned		rxDropPct;
+
+    /**
+     * Echo canceller options (see pjmedia_echo_create())
+     *
+     * Default: 0.
+     */
+    unsigned		ecOptions;
+
+    /**
+     * Echo canceller tail length, in miliseconds. Setting this to zero
+     * will disable echo cancellation.
+     *
+     * Default: PJSUA_DEFAULT_EC_TAIL_LEN
+     */
+    unsigned		ecTailLen;
+
+    /**
+     * Audio capture buffer length, in milliseconds.
+     *
+     * Default: PJMEDIA_SND_DEFAULT_REC_LATENCY
+     */
+    unsigned		sndRecLatency;
+
+    /**
+     * Audio playback buffer length, in milliseconds.
+     *
+     * Default: PJMEDIA_SND_DEFAULT_PLAY_LATENCY
+     */
+    unsigned		sndPlayLatency;
+
+    /**
+     * Jitter buffer initial prefetch delay in msec. The value must be
+     * between jb_min_pre and jb_max_pre below.
+     *
+     * Default: -1 (to use default stream settings, currently 150 msec)
+     */
+    int			jbInit;
+
+    /**
+     * Jitter buffer minimum prefetch delay in msec.
+     *
+     * Default: -1 (to use default stream settings, currently 60 msec)
+     */
+    int			jbMinPre;
+
+    /**
+     * Jitter buffer maximum prefetch delay in msec.
+     *
+     * Default: -1 (to use default stream settings, currently 240 msec)
+     */
+    int			jbMaxPre;
+
+    /**
+     * Set maximum delay that can be accomodated by the jitter buffer msec.
+     *
+     * Default: -1 (to use default stream settings, currently 360 msec)
+     */
+    int			jbMax;
+
+    /**
+     * Specify idle time of sound device before it is automatically closed,
+     * in seconds. Use value -1 to disable the auto-close feature of sound
+     * device
+     *
+     * Default : 1
+     */
+    int			sndAutoCloseTime;
+
+    /**
+     * Specify whether built-in/native preview should be used if available.
+     * In some systems, video input devices have built-in capability to show
+     * preview window of the device. Using this built-in preview is preferable
+     * as it consumes less CPU power. If built-in preview is not available,
+     * the library will perform software rendering of the input. If this
+     * field is set to PJ_FALSE, software preview will always be used.
+     *
+     * Default: PJ_TRUE
+     */
+    bool		vidPreviewEnableNative;
+
+public:
+    /** Default constructor initialises with default values */
+    MediaConfig();
+
+    /** Construct from pjsua_media_config. */
+    void fromPj(const pjsua_media_config &mc);
+
+    /** Export */
+    pjsua_media_config toPj() const;
+
+    /**
+     * Read this object from a container.
+     *
+     * @param node		Container to write values from.
+     */
+    virtual void readObject(const ContainerNode &node) throw(Error);
+
+    /**
+     * Write this object to a container.
+     *
+     * @param node		Container to write values to.
+     */
+    virtual void writeObject(ContainerNode &node) const throw(Error);
+};
+
+
+/**
+ * Endpoint configuration
+ */
+struct EpConfig : public PersistentObject
+{
+    /** UA config */
+    UaConfig		uaConfig;
+
+    /** Logging config */
+    LogConfig		logConfig;
+
+    /** Media config */
+    MediaConfig		medConfig;
+
+    /**
+     * Read this object from a container.
+     *
+     * @param node		Container to write values from.
+     */
+    virtual void readObject(const ContainerNode &node) throw(Error);
+
+    /**
+     * Write this object to a container.
+     *
+     * @param node		Container to write values to.
+     */
+    virtual void writeObject(ContainerNode &node) const throw(Error);
+
+};
+
+/* This represents posted job */
+struct PendingJob
+{
+    /** Perform the job */
+    virtual void execute(bool is_pending) = 0;
+
+    /** Virtual destructor */
+    virtual ~PendingJob() {}
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Endpoint represents an instance of pjsua library. There can only be
+ * one instance of pjsua library in an application, hence this class
+ * is a singleton.
+ */
+class Endpoint
+{
+public:
+    /** Retrieve the singleton instance of the endpoint */
+    static Endpoint &instance() throw(Error);
+
+    /** Default constructor */
+    Endpoint();
+
+    /** Virtual destructor */
+    virtual ~Endpoint();
+
+
+    /*************************************************************************
+     * Base library operations
+     */
+
+    /**
+     * Get library version.
+     */
+    Version libVersion() const;
+
+    /**
+     * Instantiate pjsua application. Application must call this function before
+     * calling any other functions, to make sure that the underlying libraries
+     * are properly initialized. Once this function has returned success,
+     * application must call destroy() before quitting.
+     */
+    void libCreate() throw(Error);
+
+    /**
+     * Get library state.
+     *
+     * @return			library state.
+     */
+    pjsua_state libGetState() const;
+
+    /**
+     * Initialize pjsua with the specified settings. All the settings are
+     * optional, and the default values will be used when the config is not
+     * specified.
+     *
+     * Note that create() MUST be called before calling this function.
+     *
+     * @param prmEpConfig	Endpoint configurations
+     */
+    void libInit( const EpConfig &prmEpConfig) throw(Error);
+
+    /**
+     * Call this function after all initialization is done, so that the
+     * library can do additional checking set up. Application may call this
+     * function any time after init().
+     */
+    void libStart() throw(Error);
+
+    /**
+     * Register a thread to poll for events. This function should be
+     * called by an external worker thread, and it will block polling
+     * for events until the library is destroyed.
+     */
+    void libRegisterWorkerThread(const string &name) throw(Error);
+
+    /**
+     * Stop all worker threads.
+     */
+    void libStopWorkerThreads();
+
+    /**
+     * Poll pjsua for events, and if necessary block the caller thread for
+     * the specified maximum interval (in miliseconds).
+     *
+     * Application doesn't normally need to call this function if it has
+     * configured worker thread (\a thread_cnt field) in pjsua_config
+     * structure, because polling then will be done by these worker threads
+     * instead.
+     *
+     * If EpConfig::UaConfig::mainThreadOnly is enabled and this function
+     * is called from the main thread (by default the main thread is thread
+     * that calls libCreate()), this function will also scan and run any
+     * pending jobs in the list.
+     *
+     * @param msec_timeout Maximum time to wait, in miliseconds.
+     *
+     * @return		The number of events that have been handled during the
+     *	    		poll. Negative value indicates error, and application
+     *	    		can retrieve the error as (status = -return_value).
+     */
+    int libHandleEvents(unsigned msec_timeout);
+
+    /**
+     * Destroy pjsua. Application is recommended to perform graceful shutdown
+     * before calling this function (such as unregister the account from the
+     * SIP server, terminate presense subscription, and hangup active calls),
+     * however, this function will do all of these if it finds there are
+     * active sessions that need to be terminated. This function will
+     * block for few seconds to wait for replies from remote.
+     *
+     * Application.may safely call this function more than once if it doesn't
+     * keep track of it's state.
+     *
+     * @param prmFlags	Combination of pjsua_destroy_flag enumeration.
+     */
+    void libDestroy(unsigned prmFlags=0) throw(Error);
+
+
+    /*************************************************************************
+     * Utilities
+     */
+
+    /**
+     * Retrieve the error string for the specified status code.
+     *
+     * @param prmErr		The error code.
+     */
+    string utilStrError(pj_status_t prmErr);
+
+    /**
+     * Write a log message.
+     *
+     * @param prmLevel		Log verbosity level (1-5)
+     * @param prmSender		The log sender.
+     * @param prmMsg		The log message.
+     */
+    void utilLogWrite(int prmLevel,
+                      const string &prmSender,
+                      const string &prmMsg);
+
+    /**
+     * Write a log entry.
+     *
+     * @param e			The log entry.
+     */
+    void utilLogWrite(LogEntry &e);
+
+    /**
+     * This is a utility function to verify that valid SIP url is given. If the
+     * URL is a valid SIP/SIPS scheme, PJ_SUCCESS will be returned.
+     *
+     * @param prmUri		The URL string.
+     *
+     * @return			PJ_SUCCESS on success, or the appropriate error
+     * 				code.
+     *
+     * @see utilVerifyUri()
+     */
+    pj_status_t utilVerifySipUri(const string &prmUri);
+
+    /**
+     * This is a utility function to verify that valid URI is given. Unlike
+     * utilVerifySipUri(), this function will return PJ_SUCCESS if tel: URI
+     * is given.
+     *
+     * @param prmUri		The URL string.
+     *
+     * @return			PJ_SUCCESS on success, or the appropriate error
+     * 				code.
+     *
+     * @see pjsua_verify_sip_url()
+     */
+    pj_status_t utilVerifyUri(const string &prmUri);
+
+    /**
+     * Schedule a timer with the specified interval and user data. When the
+     * interval elapsed, onTimer() callback will be
+     * called. Note that the callback may be executed by different thread,
+     * depending on whether worker thread is enabled or not.
+     *
+     * @param prmMsecDelay	The time interval in msec.
+     * @param prmUserData	Arbitrary user data, to be given back to
+     * 				application in the callback.
+     *
+     * @return			Token to identify the timer, which could be
+     * 				given to utilTimerCancel().
+     */
+    Token utilTimerSchedule(unsigned prmMsecDelay,
+                            Token prmUserData) throw (Error);
+
+    /**
+     * Cancel previously scheduled timer with the specified timer token.
+     *
+     * @param prmToken		The timer token, which was returned from
+     * 				previous utilTimerSchedule() call.
+     */
+    void utilTimerCancel(Token prmToken);
+
+    /**
+     * Utility to register a pending job to be executed by main thread.
+     * If EpConfig::UaConfig::mainThreadOnly is false, the job will be
+     * executed immediately.
+     *
+     * @param job		The job class.
+     */
+    void utilAddPendingJob(PendingJob *job);
+
+    /**
+     * Get cipher list supported by SSL/TLS backend.
+     */
+    IntVector utilSslGetAvailableCiphers() throw (Error);
+
+    /*************************************************************************
+     * NAT operations
+     */
+    /**
+     * 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 onNatDetectionComplete().
+     *
+     * After NAT has been detected and the callback is called, application can
+     * get the detected NAT type by calling natGetType(). Application
+     * can also perform NAT detection by calling natDetectType()
+     * again at later time.
+     *
+     * Note that STUN must be enabled to run this function successfully.
+     */
+    void natDetectType(void) throw(Error);
+
+    /**
+     * Get the NAT type as detected by natDetectType() function. This
+     * function will only return useful NAT type after natDetectType()
+     * has completed successfully and onNatDetectionComplete()
+     * callback has been called.
+     *
+     * Exception: if this function is called while detection is in progress,
+     * PJ_EPENDING exception will be raised.
+     */
+    pj_stun_nat_type natGetType() throw(Error);
+
+    /**
+     * Auxiliary function to resolve and contact each of the STUN server
+     * entries (sequentially) to find which is usable. The libInit() must
+     * have been called before calling this function.
+     *
+     * @param prmServers	Array of STUN servers to try. The endpoint
+     * 				will try to resolve and contact each of the
+     * 				STUN server entry until it finds one that is
+     * 				usable. Each entry may be a domain name, host
+     * 				name, IP address, and it may contain an
+     * 				optional port number. For example:
+     *				- "pjsip.org" (domain name)
+     *				- "sip.pjsip.org" (host name)
+     *				- "pjsip.org:33478" (domain name and a non-
+     *				   standard port number)
+     *				- "10.0.0.1:3478" (IP address and port number)
+     * @param prmWait		Specify if the function should block until
+     *				it gets the result. In this case, the function
+     *				will block while the resolution is being done,
+     *				and the callback will be called before this
+     *				function returns.
+     * @param prmUserData	Arbitrary user data to be passed back to
+     * 				application in the callback.
+     *
+     * @see natCancelCheckStunServers()
+     */
+    void natCheckStunServers(const StringVector &prmServers,
+                             bool prmWait,
+                             Token prmUserData) throw(Error);
+
+    /**
+     * Cancel pending STUN resolution which match the specified token.
+     *
+     * @param token		The token to match. This token was given to
+     *				natCheckStunServers()
+     * @param notify_cb		Boolean to control whether the callback should
+     *				be called for cancelled resolutions. When the
+     *				callback is called, the status in the result
+     *				will be set as PJ_ECANCELLED.
+     *
+     * Exception: PJ_ENOTFOUND if there is no matching one, or other error.
+     */
+    void natCancelCheckStunServers(Token token,
+                                   bool notify_cb = false) throw(Error);
+
+    /*************************************************************************
+     * Transport operations
+     */
+
+    /**
+     * Create and start a new SIP transport according to the specified
+     * settings.
+     *
+     * @param type		Transport type.
+     * @param cfg		Transport configuration.
+     *
+     * @return			The transport ID.
+     */
+    TransportId transportCreate(pjsip_transport_type_e type,
+                                const TransportConfig &cfg) throw(Error);
+
+    /**
+     * Enumerate all transports currently created in the system. This
+     * function will return all transport IDs, and application may then
+     * call transportGetInfo() function to retrieve detailed information
+     * about the transport.
+     *
+     * @return			Array of transport IDs.
+     */
+    IntVector transportEnum() throw(Error);
+
+    /**
+     * Get information about transport.
+     *
+     * @param id		Transport ID.
+     *
+     * @return			Transport info.
+     */
+    TransportInfo transportGetInfo(TransportId id) throw(Error);
+
+    /**
+     * Disable a transport or re-enable it. By default transport is always
+     * enabled after it is created. Disabling a transport does not necessarily
+     * close the socket, it will only discard incoming messages and prevent
+     * the transport from being used to send outgoing messages.
+     *
+     * @param id		Transport ID.
+     * @param enabled		Enable or disable the transport.
+     *
+     */
+    void transportSetEnable(TransportId id, bool enabled) throw(Error);
+
+    /**
+     * Close the transport. The system will wait until all transactions are
+     * closed while preventing new users from using the transport, and will
+     * close the transport when its usage count reaches zero.
+     *
+     * @param id		Transport ID.
+     */
+    void transportClose(TransportId id) throw(Error);
+
+    /*************************************************************************
+     * Call operations
+     */
+    
+    /**
+     * Terminate all calls. This will initiate call hangup for all
+     * currently active calls.
+     */
+    void hangupAllCalls(void);
+    
+    /*************************************************************************
+     * Media operations
+     */
+
+    /**
+     * Add media to the media list.
+     *
+     * @param media	media to be added.
+     */
+    void mediaAdd(AudioMedia &media);
+
+    /**
+     * Remove media from the media list.
+     *
+     * @param media	media to be removed.
+     */
+    void mediaRemove(AudioMedia &media);
+
+    /**
+     * Check if media has been added to the media list.
+     *
+     * @param media	media to be check.
+     *
+     * @return 		True if media has been added, false otherwise.
+     */
+    bool mediaExists(const AudioMedia &media) const;
+
+    /**
+     * Get maximum number of media port.
+     *
+     * @return		Maximum number of media port in the conference bridge.
+     */
+    unsigned mediaMaxPorts() const;
+
+    /**
+     * Get current number of active media port in the bridge.
+     *
+     * @return		The number of active media port.
+     */
+    unsigned mediaActivePorts() const;
+
+    /**
+     * Enumerate all media port.
+     *
+     * @return		The list of media port.
+     */
+    const AudioMediaVector &mediaEnumPorts() const throw(Error);
+
+    /**
+     * Get the instance of Audio Device Manager.
+     *
+     * @return		The Audio Device Manager.
+     */
+    AudDevManager &audDevManager();
+
+    /*************************************************************************
+     * Codec management operations
+     */
+
+    /**
+     * Enum all supported codecs in the system.
+     *
+     * @return		Array of codec info.
+     */
+    const CodecInfoVector &codecEnum() throw(Error);
+
+    /**
+     * Change codec priority.
+     *
+     * @param codec_id	Codec ID, which is a string that uniquely identify
+     *			the codec (such as "speex/8000").
+     * @param priority	Codec priority, 0-255, where zero means to disable
+     *			the codec.
+     *
+     */
+    void codecSetPriority(const string &codec_id,
+			  pj_uint8_t priority) throw(Error);
+
+    /**
+     * Get codec parameters.
+     *
+     * @param codec_id		Codec ID.
+     *
+     * @return			Codec parameters. If codec is not found, Error
+     * 				will be thrown.
+     *
+     */
+    CodecParam codecGetParam(const string &codec_id) const throw(Error);
+
+    /**
+     * Set codec parameters.
+     *
+     * @param codec_id	Codec ID.
+     * @param param	Codec parameter to set. Set to NULL to reset
+     *			codec parameter to library default settings.
+     *
+     */
+    void codecSetParam(const string &codec_id,
+		       const CodecParam param) throw(Error);
+
+
+public:
+    /*
+     * Overrideables callbacks
+     */
+
+    /**
+     * Callback when the Endpoint has finished performing NAT type
+     * detection that is initiated with natDetectType().
+     *
+     * @param prm	Callback parameters containing the detection
+     * 			result.
+     */
+    virtual void onNatDetectionComplete(
+			const OnNatDetectionCompleteParam &prm)
+    { PJ_UNUSED_ARG(prm); }
+
+    /**
+     * Callback when the Endpoint has finished performing STUN server
+     * checking that is initiated with natCheckStunServers().
+     *
+     * @param prm	Callback parameters.
+     */
+    virtual void onNatCheckStunServersComplete(
+			const OnNatCheckStunServersCompleteParam &prm)
+    { PJ_UNUSED_ARG(prm); }
+
+    /**
+     * This callback is called when transport state has changed.
+     *
+     * @param prm	Callback parameters.
+     */
+    virtual void onTransportState(
+			const OnTransportStateParam &prm)
+    { PJ_UNUSED_ARG(prm); }
+
+    /**
+     * Callback when a timer has fired. The timer was scheduled by
+     * utilTimerSchedule().
+     *
+     * @param prm	Callback parameters.
+     */
+    virtual void onTimer(const OnTimerParam &prm)
+    { PJ_UNUSED_ARG(prm); }
+
+    /**
+     * This callback can be used by application to override the account
+     * to be used to handle an incoming message. Initially, the account to
+     * be used will be calculated automatically by the library. This initial
+     * account will be used if application does not implement this callback,
+     * or application sets an invalid account upon returning from this
+     * callback.
+     *
+     * Note that currently the incoming messages requiring account assignment
+     * are INVITE, MESSAGE, SUBSCRIBE, and unsolicited NOTIFY. This callback
+     * may be called before the callback of the SIP event itself, i.e:
+     * incoming call, pager, subscription, or unsolicited-event.
+     *
+     * @param prm	Callback parameters.
+     */
+    virtual void onSelectAccount(OnSelectAccountParam &prm)
+    { PJ_UNUSED_ARG(prm); }
+
+private:
+    static Endpoint		*instance_;	// static instance
+    LogWriter			*writer;	// Custom writer, if any
+    AudioMediaVector 	 	 mediaList;
+    AudDevManager		 audioDevMgr;
+    CodecInfoVector		 codecInfoList;
+
+    /* Pending logging */
+    bool			 mainThreadOnly;
+    void			*mainThread;
+    unsigned			 pendingJobSize;
+    std::list<PendingJob*>	 pendingJobs;
+
+    void performPendingJobs();
+
+    /* Endpoint static callbacks */
+    static void logFunc(int level, const char *data, int len);
+    static void stun_resolve_cb(const pj_stun_resolve_result *result);
+    static void on_timer(pj_timer_heap_t *timer_heap,
+        		 struct pj_timer_entry *entry);
+    static void on_nat_detect(const pj_stun_nat_detect_result *res);
+    static void on_transport_state(pjsip_transport *tp,
+    				   pjsip_transport_state state,
+    				   const pjsip_transport_state_info *info);
+
+private:
+    /*
+     * Account & Call lookups
+     */
+    static Account	*lookupAcc(int acc_id, const char *op);
+    static Call		*lookupCall(int call_id, const char *op);
+
+    /* static callbacks */
+    static void on_incoming_call(pjsua_acc_id acc_id,
+                                 pjsua_call_id call_id,
+                                 pjsip_rx_data *rdata);
+    static void on_reg_started(pjsua_acc_id acc_id,
+                               pj_bool_t renew);
+    static void on_reg_state2(pjsua_acc_id acc_id,
+                              pjsua_reg_info *info);
+    static void on_incoming_subscribe(pjsua_acc_id acc_id,
+				      pjsua_srv_pres *srv_pres,
+				      pjsua_buddy_id buddy_id,
+				      const pj_str_t *from,
+				      pjsip_rx_data *rdata,
+				      pjsip_status_code *code,
+				      pj_str_t *reason,
+				      pjsua_msg_data *msg_data);
+    static void on_pager2(pjsua_call_id call_id,
+                          const pj_str_t *from,
+                          const pj_str_t *to,
+                          const pj_str_t *contact,
+                          const pj_str_t *mime_type,
+                          const pj_str_t *body,
+                          pjsip_rx_data *rdata,
+                          pjsua_acc_id acc_id);
+    static void on_pager_status2(pjsua_call_id call_id,
+				 const pj_str_t *to,
+				 const pj_str_t *body,
+				 void *user_data,
+				 pjsip_status_code status,
+				 const pj_str_t *reason,
+				 pjsip_tx_data *tdata,
+				 pjsip_rx_data *rdata,
+				 pjsua_acc_id acc_id);
+    static void on_typing2(pjsua_call_id call_id,
+                           const pj_str_t *from,
+                           const pj_str_t *to,
+                           const pj_str_t *contact,
+                           pj_bool_t is_typing,
+                           pjsip_rx_data *rdata,
+                           pjsua_acc_id acc_id);
+    static void on_mwi_info(pjsua_acc_id acc_id,
+                            pjsua_mwi_info *mwi_info);
+
+    static void on_buddy_state(pjsua_buddy_id buddy_id);
+    // Call callbacks
+    static void on_call_state(pjsua_call_id call_id, pjsip_event *e);
+    static void on_call_tsx_state(pjsua_call_id call_id,
+                                  pjsip_transaction *tsx,
+                                  pjsip_event *e);
+    static void on_call_media_state(pjsua_call_id call_id);
+    static void on_call_sdp_created(pjsua_call_id call_id,
+                                    pjmedia_sdp_session *sdp,
+                                    pj_pool_t *pool,
+                                    const pjmedia_sdp_session *rem_sdp);
+    static void on_stream_created(pjsua_call_id call_id,
+                                  pjmedia_stream *strm,
+                                  unsigned stream_idx,
+                                  pjmedia_port **p_port);
+    static void on_stream_destroyed(pjsua_call_id call_id,
+                                    pjmedia_stream *strm,
+                                    unsigned stream_idx);
+    static void on_dtmf_digit(pjsua_call_id call_id, int digit);
+    static void on_call_transfer_request(pjsua_call_id call_id,
+                                         const pj_str_t *dst,
+                                         pjsip_status_code *code);
+    static void on_call_transfer_request2(pjsua_call_id call_id,
+                                          const pj_str_t *dst,
+                                          pjsip_status_code *code,
+                                          pjsua_call_setting *opt);
+    static void on_call_transfer_status(pjsua_call_id call_id,
+                                        int st_code,
+                                        const pj_str_t *st_text,
+                                        pj_bool_t final,
+                                        pj_bool_t *p_cont);
+    static void on_call_replace_request(pjsua_call_id call_id,
+                                        pjsip_rx_data *rdata,
+                                        int *st_code,
+                                        pj_str_t *st_text);
+    static void on_call_replace_request2(pjsua_call_id call_id,
+                                         pjsip_rx_data *rdata,
+                                         int *st_code,
+                                         pj_str_t *st_text,
+                                         pjsua_call_setting *opt);
+    static void on_call_replaced(pjsua_call_id old_call_id,
+                                 pjsua_call_id new_call_id);
+    static void on_call_rx_offer(pjsua_call_id call_id,
+                                 const pjmedia_sdp_session *offer,
+                                 void *reserved,
+                                 pjsip_status_code *code,
+                                 pjsua_call_setting *opt);
+    static pjsip_redirect_op on_call_redirected(pjsua_call_id call_id,
+                                                const pjsip_uri *target,
+                                                const pjsip_event *e);
+    static pj_status_t
+    on_call_media_transport_state(pjsua_call_id call_id,
+                                  const pjsua_med_tp_state_info *info);
+    static void on_call_media_event(pjsua_call_id call_id,
+                                    unsigned med_idx,
+                                    pjmedia_event *event);
+    static pjmedia_transport*
+    on_create_media_transport(pjsua_call_id call_id,
+                              unsigned media_idx,
+                              pjmedia_transport *base_tp,
+                              unsigned flags);
+
+private:
+    void clearCodecInfoList();
+
+};
+
+
+
+/**
+ * @}  PJSUA2_UA
+ */
+
+}
+/* End pj namespace */
+
+
+#endif	/* __PJSUA2_UA_HPP__ */
+
Index: /pjproject/trunk/pjsip/include/pjsua2/json.hpp
===================================================================
--- /pjproject/trunk/pjsip/include/pjsua2/json.hpp	(revision 4704)
+++ /pjproject/trunk/pjsip/include/pjsua2/json.hpp	(revision 4704)
@@ -0,0 +1,116 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2013 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 __PJSUA2_JSON_HPP__
+#define __PJSUA2_JSON_HPP__
+
+/**
+ * @file pjsua2/persistent.hpp
+ * @brief PJSUA2 Persistent Services
+ */
+#include <pjsua2/persistent.hpp>
+#include <pjlib-util/json.h>
+#include <pj/pool.h>
+#include <string>
+
+/** PJSUA2 API is inside pj namespace */
+namespace pj
+{
+
+/**
+ * @defgroup PJSUA2_JSON JSON Persistent Support
+ * @ingroup PJSUA2_PERSISTENT
+ * @{
+ * Provides object serialization and deserialization to/from JSON document.
+ */
+
+using std::string;
+
+/**
+ * Persistent document (file) with JSON format.
+ */
+class JsonDocument : public PersistentDocument
+{
+public:
+    /** Default constructor */
+    JsonDocument();
+
+    /** Destructor */
+    ~JsonDocument();
+
+    /**
+     * Load this document from a file.
+     *
+     * @param filename		The file name.
+     */
+    virtual void   loadFile(const string &filename) throw(Error);
+
+    /**
+     * Load this document from string.
+     *
+     * @param input		The string.
+     */
+    virtual void   loadString(const string &input) throw(Error);
+
+    /**
+     * Write this document to a file.
+     *
+     * @param filename		The file name.
+     */
+    virtual void   saveFile(const string &filename) throw(Error);
+
+    /**
+     * Write this document to string.
+     */
+    virtual string saveString() throw(Error);
+
+    /**
+     * Get the root container node for this document
+     */
+    virtual ContainerNode & getRootContainer() const;
+
+    /**
+     * An internal function to create JSON element.
+     */
+    pj_json_elem*    allocElement() const;
+
+    /**
+     * An internal function to get the pool.
+     */
+    pj_pool_t*	     getPool();
+
+private:
+    pj_caching_pool	  cp;
+    mutable ContainerNode rootNode;
+    mutable pj_json_elem *root;
+    mutable pj_pool_t	 *pool;
+
+    void initRoot() const;
+};
+
+
+
+
+/**
+ * @}  PJSUA2
+ */
+
+} // namespace pj
+
+
+#endif	/* __PJSUA2_JSON_HPP__ */
Index: /pjproject/trunk/pjsip/include/pjsua2/media.hpp
===================================================================
--- /pjproject/trunk/pjsip/include/pjsua2/media.hpp	(revision 4704)
+++ /pjproject/trunk/pjsip/include/pjsua2/media.hpp	(revision 4704)
@@ -0,0 +1,1162 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2013 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 __PJSUA2_MEDIA_HPP__
+#define __PJSUA2_MEDIA_HPP__
+
+/**
+ * @file pjsua2/media.hpp
+ * @brief PJSUA2 media operations
+ */
+#include <pjsua-lib/pjsua.h>
+#include <pjsua2/types.hpp>
+
+/** PJSUA2 API is inside pj namespace */
+namespace pj
+{
+
+/**
+ * @defgroup PJSUA2_MED Media
+ * @ingroup PJSUA2_Ref
+ * @{
+ */
+
+using std::string;
+using std::vector;
+
+/**
+ * This structure contains all the information needed to completely describe
+ * a media.
+ */
+struct MediaFormat
+{
+    /**
+     * The format id that specifies the audio sample or video pixel format.
+     * Some well known formats ids are declared in pjmedia_format_id
+     * enumeration.
+     *
+     * @see pjmedia_format_id
+     */
+    pj_uint32_t		id;
+
+    /**
+     * The top-most type of the media, as an information.
+     */
+    pjmedia_type	type;
+};
+
+/**
+ * This structure describe detail information about an audio media.
+ */
+struct MediaFormatAudio : public MediaFormat
+{
+    unsigned	clockRate;	/**< Audio clock rate in samples or Hz. */
+    unsigned	channelCount;	/**< Number of channels.		*/
+    unsigned	frameTimeUsec;  /**< Frame interval, in microseconds.	*/
+    unsigned	bitsPerSample;	/**< Number of bits per sample.		*/
+    pj_uint32_t	avgBps;		/**< Average bitrate			*/
+    pj_uint32_t	maxBps;		/**< Maximum bitrate			*/
+
+    /**
+     * Construct from pjmedia_format.
+     */
+    void fromPj(const pjmedia_format &format);
+
+    /**
+     * Export to pjmedia_format.
+     */
+    pjmedia_format toPj() const;
+};
+
+/**
+ * This structure describe detail information about an video media.
+ */
+struct MediaFormatVideo : public MediaFormat
+{
+    unsigned		width;	    /**< Video width. 	*/
+    unsigned		height;	    /**< Video height. 	*/
+    int			fpsNum;	    /**< Frames per second numerator.	*/
+    int			fpsDenum;   /**< Frames per second denumerator.	*/
+    pj_uint32_t		avgBps;	    /**< Average bitrate.		*/
+    pj_uint32_t		maxBps;	    /**< Maximum bitrate.		*/
+};
+
+/** Array of MediaFormat */
+typedef std::vector<MediaFormat*> MediaFormatVector;
+
+/**
+ * This structure descibes information about a particular media port that
+ * has been registered into the conference bridge. 
+ */
+struct ConfPortInfo
+{
+    /**
+     * Conference port number.
+     */
+    int			portId;
+
+    /**
+     * Port name.
+     */
+    string		name;
+
+    /**
+     * Media audio format information
+     */
+    MediaFormatAudio	format;
+
+    /**
+     * Tx level adjustment. Value 1.0 means no adjustment, value 0 means
+     * the port is muted, value 2.0 means the level is amplified two times.
+     */
+    float		txLevelAdj;
+
+    /**
+     * Rx level adjustment. Value 1.0 means no adjustment, value 0 means
+     * the port is muted, value 2.0 means the level is amplified two times.
+     */
+    float		rxLevelAdj;
+
+    /**
+     * Array of listeners (in other words, ports where this port is
+     * transmitting to.
+     */
+    IntVector		listeners;
+
+public:
+    /**
+     * Construct from pjsua_conf_port_info.
+     */
+    void fromPj(const pjsua_conf_port_info &port_info);
+};
+
+/**
+ * Media port, corresponds to pjmedia_port
+ */
+typedef void *MediaPort;
+
+/**
+ * Media.
+ */
+class Media
+{
+public:
+    /**
+     * Virtual destructor.
+     */
+    virtual ~Media();
+
+    /**
+     * Get type of the media.
+     *
+     * @return          The media type.
+     */
+    pjmedia_type getType() const;
+
+protected:
+    /**
+     * Constructor.
+     */
+    Media(pjmedia_type med_type);
+
+private:
+    /**
+     * Media type.
+     */
+    pjmedia_type        type;
+};
+
+/**
+ * Audio Media.
+ */
+class AudioMedia : public Media
+{
+public:
+    /**
+    * Get information about the specified conference port.
+    */
+    ConfPortInfo getPortInfo() const throw(Error);
+
+    /**
+     * Get port Id.
+     */
+    int getPortId() const;
+
+    /**
+     * Get information from specific port id.
+     */
+    static ConfPortInfo getPortInfoFromId(int port_id) throw(Error);
+
+    /**
+     * Establish unidirectional media flow to sink. This media port
+     * will act as a source, and it may transmit to multiple destinations/sink.
+     * And if multiple sources are transmitting to the same sink, the media
+     * will be mixed together. Source and sink may refer to the same Media,
+     * effectively looping the media.
+     *
+     * If bidirectional media flow is desired, application needs to call
+     * this method twice, with the second one called from the opposite source
+     * media.
+     *
+     * @param sink		The destination Media.
+     */
+    void startTransmit(const AudioMedia &sink) const throw(Error);
+
+    /**
+     *  Stop media flow to destination/sink port.
+     *
+     * @param sink		The destination media.
+     *
+     */
+    void stopTransmit(const AudioMedia &sink) const throw(Error);
+
+    /**
+     * Adjust the signal level to be transmitted from the bridge to this
+     * media port by making it louder or quieter.
+     *
+     * @param level		Signal level adjustment. Value 1.0 means no
+     *				level adjustment, while value 0 means to mute
+     *				the port.
+     */
+    void adjustRxLevel(float level) throw(Error);
+
+    /**
+     * Adjust the signal level to be received from this media port (to
+     * the bridge) by making it louder or quieter.
+     *
+     * @param level		Signal level adjustment. Value 1.0 means no
+     *				level adjustment, while value 0 means to mute
+     *				the port.
+     */
+    void adjustTxLevel(float level) throw(Error);
+
+    /**
+     * Get the last received signal level.
+     *
+     * @return			Signal level in percent.
+     */
+    unsigned getRxLevel() const throw(Error);
+
+    /**
+     * Get the last transmitted signal level.
+     *
+     * @return			Signal level in percent.
+     */
+    unsigned getTxLevel() const throw(Error);
+
+    /**
+     * Typecast from base class Media. This is useful for application written
+     * in language that does not support downcasting such as Python.
+     *
+     * @param media		The object to be downcasted
+     *
+     * @return			The object as AudioMedia instance
+     */
+    static AudioMedia* typecastFromMedia(Media *media);
+
+    /**
+     * Virtual Destructor
+     */
+    virtual ~AudioMedia();
+
+protected:
+    /**
+     * Conference port Id.
+     */
+    int			 id;
+
+protected:
+    /**
+     * Default Constructor.
+     */
+    AudioMedia();
+
+    /**
+     * This method needs to be called by descendants of this class to register
+     * the media port created to the conference bridge and Endpoint's
+     * media list.
+     *
+     * param port  the media port to be registered to the conference bridge.
+     *
+     */
+    void registerMediaPort(MediaPort port) throw(Error);
+
+    /**
+     * This method needs to be called by descendants of this class to remove
+     * the media port from the conference bridge and Endpoint's media list.
+     * Descendant should only call this method if it has registered the media
+     * with the previous call to registerMediaPort().
+     */
+    void unregisterMediaPort();
+
+private:
+    pj_caching_pool 	 mediaCachingPool;
+    pj_pool_t 		*mediaPool;
+
+private:
+    unsigned getSignalLevel(bool is_rx = true) const throw(Error);
+};
+
+/** Array of Audio Media */
+typedef std::vector<AudioMedia*> AudioMediaVector;
+
+/**
+ * Audio Media Player.
+ */
+class AudioMediaPlayer : public AudioMedia
+{
+public:
+    /** 
+     * Constructor.
+     */
+    AudioMediaPlayer();
+
+    /**
+     * Create a file player,  and automatically add this 
+     * player to the conference bridge.
+     *
+     * @param file_name	 The filename to be played. Currently only
+     *			 WAV files are supported, and the WAV file MUST be
+     *			 formatted as 16bit PCM mono/single channel (any
+     *			 clock rate is supported).
+     * @param options	 Optional option flag. Application may specify
+     *			 PJMEDIA_FILE_NO_LOOP to prevent playback loop.
+     */
+    void createPlayer(const string &file_name,
+		      unsigned options=PJMEDIA_FILE_NO_LOOP) throw(Error);
+
+    /**
+     * Create a file playlist media port, and automatically add the port
+     * to the conference bridge.
+     *
+     * @param file_names  Array of file names to be added to the play list.
+     *			  Note that the files must have the same clock rate,
+     *			  number of channels, and number of bits per sample.
+     * @param label	  Optional label to be set for the media port.
+     * @param options	  Optional option flag. Application may specify
+     *			  PJMEDIA_FILE_NO_LOOP to prevent looping.
+     */
+    void createPlaylist(const StringVector &file_names,
+			const string &label="",
+			unsigned options=PJMEDIA_FILE_NO_LOOP) throw(Error);
+
+    /**
+     * Set playback position. This operation is not valid for playlist.
+     */
+    void setPos(pj_uint32_t samples) throw(Error);
+
+    /**
+     * Typecast from base class AudioMedia. This is useful for application
+     * written in language that does not support downcasting such as Python.
+     *
+     * @param media		The object to be downcasted
+     *
+     * @return			The object as AudioMediaPlayer instance
+     */
+    static AudioMediaPlayer* typecastFromAudioMedia(AudioMedia *media);
+
+    /**
+     * Virtual destructor.
+     */
+    virtual ~AudioMediaPlayer();
+
+private:
+    /**
+     * Player Id.
+     */
+    int	playerId;
+
+};
+
+/**
+ * Audio Media Recorder.
+ */
+class AudioMediaRecorder : public AudioMedia
+{
+public:
+    /**
+     * Constructor.
+     */
+    AudioMediaRecorder();
+
+    /**
+     * Create a file recorder, and automatically connect this recorder to
+     * the conference bridge. The recorder currently supports recording WAV
+     * file. The type of the recorder to use is determined by the extension of
+     * the file (e.g. ".wav").
+     *
+     * @param file_name	 Output file name. The function will determine the
+     *			 default format to be used based on the file extension.
+     *			 Currently ".wav" is supported on all platforms.
+     * @param enc_type	 Optionally specify the type of encoder to be used to
+     *			 compress the media, if the file can support different
+     *			 encodings. This value must be zero for now.
+     * @param max_size	 Maximum file size. Specify zero or -1 to remove size
+     *			 limitation. This value must be zero or -1 for now.
+     * @param options	 Optional options.
+     */
+    void createRecorder(const string &file_name,
+			unsigned enc_type=0,
+			pj_ssize_t max_size=0,
+			unsigned options=PJMEDIA_FILE_WRITE_PCM) throw(Error);
+
+    /**
+     * Typecast from base class AudioMedia. This is useful for application
+     * written in language that does not support downcasting such as Python.
+     *
+     * @param media		The object to be downcasted
+     *
+     * @return			The object as AudioMediaRecorder instance
+     */
+    static AudioMediaRecorder* typecastFromAudioMedia(AudioMedia *media);
+
+    /**
+     * Virtual destructor.
+     */
+    virtual ~AudioMediaRecorder();
+
+private:
+    /**
+     * Recorder Id.
+     */
+    int	recorderId;
+};
+
+/*************************************************************************
+* Sound device management
+*/
+
+/**
+ * Audio device information structure.
+ */
+struct AudioDevInfo
+{
+    /**
+     * The device name
+     */
+    string name;
+
+    /**
+     * Maximum number of input channels supported by this device. If the
+     * value is zero, the device does not support input operation (i.e.
+     * it is a playback only device).
+     */
+    unsigned inputCount;
+
+    /**
+     * Maximum number of output channels supported by this device. If the
+     * value is zero, the device does not support output operation (i.e.
+     * it is an input only device).
+     */
+    unsigned outputCount;
+
+    /**
+     * Default sampling rate.
+     */
+    unsigned defaultSamplesPerSec;
+
+    /**
+     * The underlying driver name
+     */
+    string driver;
+
+    /**
+     * Device capabilities, as bitmask combination of pjmedia_aud_dev_cap.
+     */
+    unsigned caps;
+
+    /**
+     * Supported audio device routes, as bitmask combination of
+     * pjmedia_aud_dev_route. The value may be zero if the device
+     * does not support audio routing.
+     */
+    unsigned routes;
+
+    /**
+     * Array of supported extended audio formats
+     */
+    MediaFormatVector extFmt;
+
+    /**
+     * Construct from pjmedia_aud_dev_info.
+     */
+    void fromPj(const pjmedia_aud_dev_info &dev_info);
+
+    /**
+     * Destructor.
+     */
+    ~AudioDevInfo();
+};
+
+/** Array of audio device info */
+typedef std::vector<AudioDevInfo*> AudioDevInfoVector;
+
+/**
+ * Audio device manager.
+ */
+class AudDevManager
+{
+public:
+    /**
+     * Get currently active capture sound devices. If sound devices has not been
+     * created, it is possible that the function returns -1 as device IDs.
+     *
+     * @return 			Device ID of the capture device.
+     */
+    int getCaptureDev() const throw(Error);
+
+    /**
+     * Get the AudioMedia of the capture audio device.
+     *
+     * @return			Audio media for the capture device.
+     */
+    AudioMedia &getCaptureDevMedia() throw(Error);
+
+    /**
+     * Get currently active playback sound devices. If sound devices has not
+     * been created, it is possible that the function returns -1 as device IDs.
+     *
+     * @return 			Device ID of the playback device.
+     */
+    int getPlaybackDev() const throw(Error);
+
+    /**
+     * Get the AudioMedia of the speaker/playback audio device.
+     *
+     * @return			Audio media for the speaker/playback device.
+     */
+    AudioMedia &getPlaybackDevMedia() throw(Error);
+
+    /**
+     * Select or change capture sound device. Application may call this
+     * function at any time to replace current sound device.
+     *
+     * @param capture_dev   	Device ID of the capture device.
+     */
+    void setCaptureDev(int capture_dev) const throw(Error);
+
+    /**
+     * Select or change playback sound device. Application may call this
+     * function at any time to replace current sound device.
+     *
+     * @param playback_dev   	Device ID of the playback device.
+     */
+    void setPlaybackDev(int playback_dev) const throw(Error);
+
+    /**
+     * Enum all audio devices installed in the system.
+     *
+     * @return			The list of audio device info.
+     */
+    const AudioDevInfoVector &enumDev() throw(Error);
+
+    /**
+     * Set pjsua to use null sound device. The null sound device only provides
+     * the timing needed by the conference bridge, and will not interract with
+     * any hardware.
+     *
+     */
+    void setNullDev() throw(Error);
+
+    /**
+     * Disconnect the main conference bridge from any sound devices, and let
+     * application connect the bridge to it's own sound device/master port.
+     *
+     * @return			The port interface of the conference bridge,
+     *				so that application can connect this to it's
+     *				own sound device or master port.
+     */
+    MediaPort *setNoDev();
+
+    /**
+     * Change the echo cancellation settings.
+     *
+     * The behavior of this function depends on whether the sound device is
+     * currently active, and if it is, whether device or software AEC is
+     * being used.
+     *
+     * If the sound device is currently active, and if the device supports AEC,
+     * this function will forward the change request to the device and it will
+     * be up to the device on whether support the request. If software AEC is
+     * being used (the software EC will be used if the device does not support
+     * AEC), this function will change the software EC settings. In all cases,
+     * the setting will be saved for future opening of the sound device.
+     *
+     * If the sound device is not currently active, this will only change the
+     * default AEC settings and the setting will be applied next time the
+     * sound device is opened.
+     *
+     * @param tail_msec		The tail length, in miliseconds. Set to zero to
+     *				disable AEC.
+     * @param options		Options to be passed to pjmedia_echo_create().
+     *				Normally the value should be zero.
+     *
+     */
+    void setEcOptions(unsigned tail_msec, unsigned options) throw(Error);
+
+    /**
+     * Get current echo canceller tail length.
+     *
+     * @return			The EC tail length in milliseconds,
+     *				If AEC is disabled, the value will be zero.
+     */
+    unsigned getEcTail() const throw(Error);
+
+    /**
+     * Check whether the sound device is currently active. The sound device
+     * may be inactive if the application has set the auto close feature to
+     * non-zero (the sndAutoCloseTime setting in MediaConfig), or
+     * if null sound device or no sound device has been configured via the
+     * setNoDev() function.
+     */
+    bool sndIsActive() const;
+
+    /**
+     * Refresh the list of sound devices installed in the system. This method
+     * will only refresh the list of audio device so all active audio streams
+     * will be unaffected. After refreshing the device list, application MUST
+     * make sure to update all index references to audio devices before calling
+     * any method that accepts audio device index as its parameter.
+     *
+     */
+    void refreshDevs() throw(Error);
+
+    /**
+     * Get the number of sound devices installed in the system.
+     *
+     * @return 			The number of sound devices installed in the
+     * 				system.
+     *
+     */
+    unsigned getDevCount() const;
+
+    /**
+     * Get device information.
+     *
+     * @param id		The audio device ID.
+     *
+     * @return			The device information which will be filled in
+     * 				by this method once it returns successfully.
+     */
+    AudioDevInfo getDevInfo(int id) const throw(Error);
+
+    /**
+     * Lookup device index based on the driver and device name.
+     *
+     * @param drv_name		The driver name.
+     * @param dev_name		The device name.
+     *
+     * @return			The device ID. If the device is not found,
+     * 				Error will be thrown.
+     */
+    int lookupDev(const string &drv_name,
+		  const string &dev_name) const throw(Error);
+
+    /**
+     * Get string info for the specified capability.
+     *
+     * @param cap		The capability ID.
+     *
+     * @return			Capability name.
+     */
+    string capName(pjmedia_aud_dev_cap cap) const;
+
+    /**
+     * This will configure audio format capability (other than PCM) to the
+     * sound device being used. If sound device is currently active, the method
+     * will forward the setting to the sound device instance to be applied
+     * immediately, if it supports it.
+     *
+     * This method is only valid if the device has
+     * PJMEDIA_AUD_DEV_CAP_EXT_FORMAT capability in AudioDevInfo.caps flags,
+     * otherwise Error will be thrown.
+     *
+     * Note that in case the setting is kept for future use, it will be applied
+     * to any devices, even when application has changed the sound device to be
+     * used.
+     *
+     * @param format		The audio format.
+     * @param keep		Specify whether the setting is to be kept for
+     * 				future use.
+     *
+     */
+    void
+    setExtFormat(const MediaFormatAudio &format, bool keep=true) throw(Error);
+
+    /**
+     * Get the audio format capability (other than PCM) of the sound device
+     * being used. If sound device is currently active, the method will forward
+     * the request to the sound device. If sound device is currently inactive,
+     * and if application had previously set the setting and mark the setting
+     * as kept, then that setting will be returned. Otherwise, this method
+     * will raise error.
+     *
+     * This method is only valid if the device has
+     * PJMEDIA_AUD_DEV_CAP_EXT_FORMAT capability in AudioDevInfo.caps flags,
+     * otherwise Error will be thrown.
+     *
+     * @return	    		The audio format.
+     *
+     */
+    MediaFormatAudio getExtFormat() const throw(Error);
+
+    /**
+     * This will configure audio input latency control or query capability to
+     * the sound device being used. If sound device is currently active,
+     * the method will forward the setting to the sound device instance to be
+     * applied immediately, if it supports it.
+     *
+     * This method is only valid if the device has
+     * PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY capability in AudioDevInfo.caps flags,
+     * otherwise Error will be thrown.
+     *
+     * Note that in case the setting is kept for future use, it will be applied
+     * to any devices, even when application has changed the sound device to be
+     * used.
+     *
+     * @param latency_msec	The input latency.
+     * @param keep		Specify whether the setting is to be kept
+     *				for future use.
+     */
+    void
+    setInputLatency(unsigned latency_msec, bool keep=true) throw(Error);
+
+    /**
+     * Get the audio input latency control or query capability of the sound
+     * device being used. If sound device is currently active, the method will
+     * forward the request to the sound device. If sound device is currently
+     * inactive, and if application had previously set the setting and mark the
+     * setting as kept, then that setting will be returned. Otherwise, this
+     * method will raise error.
+     *
+     * This method is only valid if the device has
+     * PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY capability in AudioDevInfo.caps flags,
+     * otherwise Error will be thrown.
+     *
+     * @return	    		The audio input latency.
+     *
+     */
+    unsigned getInputLatency() const throw(Error);
+
+    /**
+     * This will configure audio output latency control or query capability to
+     * the sound device being used. If sound device is currently active,
+     * the method will forward the setting to the sound device instance to be
+     * applied immediately, if it supports it.
+     *
+     * This method is only valid if the device has
+     * PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY capability in AudioDevInfo.caps flags,
+     * otherwise Error will be thrown.
+     *
+     * Note that in case the setting is kept for future use, it will be applied
+     * to any devices, even when application has changed the sound device to be
+     * used.
+     *
+     * @param latency_msec    	The output latency.
+     * @param keep		Specify whether the setting is to be kept
+     * 				for future use.
+     *
+     */
+    void
+    setOutputLatency(unsigned latency_msec, bool keep=true) throw(Error);
+
+    /**
+     * Get the audio output latency control or query capability of the sound
+     * device being used. If sound device is currently active, the method will
+     * forward the request to the sound device. If sound device is currently
+     * inactive, and if application had previously set the setting and mark the
+     * setting as kept, then that setting will be returned. Otherwise, this
+     * method will raise error.
+     *
+     * This method is only valid if the device has
+     * PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY capability in AudioDevInfo.caps flags,
+     * otherwise Error will be thrown.
+     *
+     * @return	    		The audio output latency.
+     *
+     */
+    unsigned getOutputLatency() const throw(Error);
+
+    /**
+     * This will configure audio input volume level capability to the
+     * sound device being used.
+     * If sound device is currently active, the method will forward the
+     * setting to the sound device instance to be applied immediately,
+     * if it supports it.
+     *
+     * This method is only valid if the device has
+     * PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING capability in AudioDevInfo.caps
+     * flags, otherwise Error will be thrown.
+     *
+     * Note that in case the setting is kept for future use, it will be applied
+     * to any devices, even when application has changed the sound device to be
+     * used.
+     *
+     * @param volume		The input volume level, in percent.
+     * @param keep		Specify whether the setting is to be kept for
+     * 				future use.
+     *
+     */
+    void setInputVolume(unsigned volume, bool keep=true) throw(Error);
+
+    /**
+     * Get the audio input volume level capability of the sound device being
+     * used. If sound device is currently active, the method will forward the
+     * request to the sound device. If sound device is currently inactive,
+     * and if application had previously set the setting and mark the setting
+     * as kept, then that setting will be returned. Otherwise, this method
+     * will raise error.
+     *
+     * This method is only valid if the device has
+     * PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING capability in AudioDevInfo.caps
+     * flags, otherwise Error will be thrown.     *
+
+     * @return	    		The audio input volume level, in percent.
+     *
+     */
+    unsigned getInputVolume() const throw(Error);
+
+    /**
+     * This will configure audio output volume level capability to the sound
+     * device being used. If sound device is currently active, the method will
+     * forward the setting to the sound device instance to be applied
+     * immediately, if it supports it.
+     *
+     * This method is only valid if the device has
+     * PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING capability in AudioDevInfo.caps
+     * flags, otherwise Error will be thrown.
+     *
+     * Note that in case the setting is kept for future use, it will be applied
+     * to any devices, even when application has changed the sound device to be
+     * used.
+     *
+     * @param volume		The output volume level, in percent.
+     * @param keep		Specify whether the setting is to be kept
+     * 				for future use.
+     *
+     */
+    void setOutputVolume(unsigned volume, bool keep=true) throw(Error);
+
+    /**
+     * Get the audio output volume level capability of the sound device being
+     * used. If sound device is currently active, the method will forward the
+     * request to the sound device. If sound device is currently inactive,
+     * and if application had previously set the setting and mark the setting
+     * as kept, then that setting will be returned. Otherwise, this method
+     * will raise error.
+     *
+     * This method is only valid if the device has
+     * PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING capability in AudioDevInfo.caps
+     * flags, otherwise Error will be thrown.
+     *
+     * @return	    		The audio output volume level, in percent.
+     *
+     */
+    unsigned getOutputVolume() const throw(Error);
+
+    /**
+     * Get the audio input signal level capability of the sound device being
+     * used. If sound device is currently active, the method will forward the
+     * request to the sound device. If sound device is currently inactive,
+     * and if application had previously set the setting and mark the setting
+     * as kept, then that setting will be returned. Otherwise, this method
+     * will raise error.
+     *
+     * This method is only valid if the device has
+     * PJMEDIA_AUD_DEV_CAP_INPUT_SIGNAL_METER capability in AudioDevInfo.caps
+     * flags, otherwise Error will be thrown.
+     *
+     * @return	    		The audio input signal level, in percent.
+     *
+     */
+    unsigned getInputSignal() const throw(Error);
+
+    /**
+     * Get the audio output signal level capability of the sound device being
+     * used. If sound device is currently active, the method will forward the
+     * request to the sound device. If sound device is currently inactive,
+     * and if application had previously set the setting and mark the setting
+     * as kept, then that setting will be returned. Otherwise, this method
+     * will raise error.
+     *
+     * This method is only valid if the device has
+     * PJMEDIA_AUD_DEV_CAP_OUTPUT_SIGNAL_METER capability in AudioDevInfo.caps
+     * flags, otherwise Error will be thrown.
+     *
+     * @return	    		The audio output signal level, in percent.
+     *
+     */
+    unsigned getOutputSignal() const throw(Error);
+
+    /**
+     * This will configure audio input route capability to the sound device
+     * being used. If sound device is currently active, the method will
+     * forward the setting to the sound device instance to be applied
+     * immediately, if it supports it.
+     *
+     * This method is only valid if the device has
+     * PJMEDIA_AUD_DEV_CAP_INPUT_ROUTE capability in AudioDevInfo.caps
+     * flags, otherwise Error will be thrown.
+     *
+     * Note that in case the setting is kept for future use, it will be applied
+     * to any devices, even when application has changed the sound device to be
+     * used.
+     *
+     * @param route		The audio input route.
+     * @param keep		Specify whether the setting is to be kept
+     * 				for future use.
+     *
+     */
+    void
+    setInputRoute(pjmedia_aud_dev_route route, bool keep=true) throw(Error);
+
+    /**
+     * Get the audio input route capability of the sound device being used.
+     * If sound device is currently active, the method will forward the
+     * request to the sound device. If sound device is currently inactive,
+     * and if application had previously set the setting and mark the setting
+     * as kept, then that setting will be returned. Otherwise, this method
+     * will raise error.
+     *
+     * This method is only valid if the device has
+     * PJMEDIA_AUD_DEV_CAP_INPUT_ROUTE capability in AudioDevInfo.caps
+     * flags, otherwise Error will be thrown.
+     *
+     * @return	    		The audio input route.
+     *
+     */
+    pjmedia_aud_dev_route getInputRoute() const throw(Error);
+
+    /**
+     * This will configure audio output route capability to the sound device
+     * being used. If sound device is currently active, the method will
+     * forward the setting to the sound device instance to be applied
+     * immediately, if it supports it.
+     *
+     * This method is only valid if the device has
+     * PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE capability in AudioDevInfo.caps
+     * flags, otherwise Error will be thrown.
+     *
+     * Note that in case the setting is kept for future use, it will be applied
+     * to any devices, even when application has changed the sound device to be
+     * used.
+     *
+     * @param route		The audio output route.
+     * @param keep		Specify whether the setting is to be kept
+     * 				for future use.
+     *
+     */
+    void
+    setOutputRoute(pjmedia_aud_dev_route route, bool keep=true) throw(Error);
+
+    /**
+     * Get the audio output route capability of the sound device being used.
+     * If sound device is currently active, the method will forward the
+     * request to the sound device. If sound device is currently inactive,
+     * and if application had previously set the setting and mark the setting
+     * as kept, then that setting will be returned. Otherwise, this method
+     * will raise error.
+     *
+     * This method is only valid if the device has
+     * PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE capability in AudioDevInfo.caps
+     * flags, otherwise Error will be thrown.
+     *
+     * @return	    		The audio output route.
+     *
+     */
+    pjmedia_aud_dev_route getOutputRoute() const throw(Error);
+
+    /**
+     * This will configure audio voice activity detection capability to
+     * the sound device being used. If sound device is currently active,
+     * the method will forward the setting to the sound device instance
+     * to be applied immediately, if it supports it.
+     *
+     * This method is only valid if the device has PJMEDIA_AUD_DEV_CAP_VAD
+     * capability in AudioDevInfo.caps flags, otherwise Error will be thrown.
+     *
+     * Note that in case the setting is kept for future use, it will be applied
+     * to any devices, even when application has changed the sound device to be
+     * used.
+     *
+     * @param enable		Enable/disable voice activity detection
+     *				feature. Set true to enable.
+     * @param keep		Specify whether the setting is to be kept for
+     *				future use.
+     *
+     */
+    void setVad(bool enable, bool keep=true) throw(Error);
+
+    /**
+     * Get the audio voice activity detection capability of the sound device
+     * being used. If sound device is currently active, the method will
+     * forward the request to the sound device. If sound device is currently
+     * inactive, and if application had previously set the setting and mark
+     * the setting as kept, then that setting will be returned. Otherwise,
+     * this method will raise error.
+     *
+     * This method is only valid if the device has PJMEDIA_AUD_DEV_CAP_VAD
+     * capability in AudioDevInfo.caps flags, otherwise Error will be thrown.
+     *
+     * @return	    		The audio voice activity detection feature.
+     *
+     */
+    bool getVad() const throw(Error);
+
+    /**
+     * This will configure audio comfort noise generation capability to
+     * the sound device being used. If sound device is currently active,
+     * the method will forward the setting to the sound device instance
+     * to be applied immediately, if it supports it.
+     *
+     * This method is only valid if the device has PJMEDIA_AUD_DEV_CAP_CNG
+     * capability in AudioDevInfo.caps flags, otherwise Error will be thrown.
+     *
+     * Note that in case the setting is kept for future use, it will be applied
+     * to any devices, even when application has changed the sound device to be
+     * used.
+     *
+     * @param enable		Enable/disable comfort noise generation
+     *				feature. Set true to enable.
+     * @param keep		Specify whether the setting is to be kept for
+     *				future use.
+     *
+     */
+    void setCng(bool enable, bool keep=true) throw(Error);
+
+    /**
+     * Get the audio comfort noise generation capability of the sound device
+     * being used. If sound device is currently active, the method will
+     * forward the request to the sound device. If sound device is currently
+     * inactive, and if application had previously set the setting and mark
+     * the setting as kept, then that setting will be returned. Otherwise,
+     * this method will raise error.
+     *
+     * This method is only valid if the device has PJMEDIA_AUD_DEV_CAP_CNG
+     * capability in AudioDevInfo.caps flags, otherwise Error will be thrown.
+     *
+     * @return	    		The audio comfort noise generation feature.
+     *
+     */
+    bool getCng() const throw(Error);
+
+    /**
+     * This will configure audio packet loss concealment capability to
+     * the sound device being used. If sound device is currently active,
+     * the method will forward the setting to the sound device instance
+     * to be applied immediately, if it supports it.
+     *
+     * This method is only valid if the device has PJMEDIA_AUD_DEV_CAP_PLC
+     * capability in AudioDevInfo.caps flags, otherwise Error will be thrown.
+     *
+     * Note that in case the setting is kept for future use, it will be applied
+     * to any devices, even when application has changed the sound device to be
+     * used.
+     *
+     * @param enable		Enable/disable packet loss concealment
+     *				feature. Set true to enable.
+     * @param keep		Specify whether the setting is to be kept for
+     *				future use.
+     *
+     */
+    void setPlc(bool enable, bool keep=true) throw(Error);
+
+    /**
+     * Get the audio packet loss concealment capability of the sound device
+     * being used. If sound device is currently active, the method will
+     * forward the request to the sound device. If sound device is currently
+     * inactive, and if application had previously set the setting and mark
+     * the setting as kept, then that setting will be returned. Otherwise,
+     * this method will raise error.
+     *
+     * This method is only valid if the device has PJMEDIA_AUD_DEV_CAP_PLC
+     * capability in AudioDevInfo.caps flags, otherwise Error will be thrown.
+     *
+     * @return	    		The audio packet loss concealment feature.
+     *
+     */
+    bool getPlc() const throw(Error);
+
+private:
+    AudioDevInfoVector		 audioDevList;
+    AudioMedia			*devMedia;
+
+    /**
+     * Constructor.
+     */
+    AudDevManager();
+
+    /**
+     * Destructor.
+     */
+    ~AudDevManager();
+
+    void clearAudioDevList();
+    int getActiveDev(bool is_capture) const throw(Error);
+
+    friend class Endpoint;
+};
+
+/*************************************************************************
+* Codec management
+*/
+
+/**
+ * This structure describes codec information.
+ */
+struct CodecInfo
+{
+    /**
+     * Codec unique identification.
+     */
+    string		codecId;
+
+    /**
+     * Codec priority (integer 0-255).
+     */
+    pj_uint8_t		priority;
+
+    /**
+     * Codec description.
+     */
+    string		desc;
+
+    /**
+     * Construct from pjsua_codec_info.
+     */
+    void fromPj(const pjsua_codec_info &codec_info);
+};
+
+/** Array of codec info */
+typedef std::vector<CodecInfo*> CodecInfoVector;
+
+/**
+ * Codec parameters, corresponds to pjmedia_codec_param or
+ * pjmedia_vid_codec_param.
+ */
+typedef void *CodecParam;
+
+
+/**
+ * @}  // PJSUA2_MED
+ */
+
+} // namespace pj
+
+#endif	/* __PJSUA2_MEDIA_HPP__ */
Index: /pjproject/trunk/pjsip/include/pjsua2/persistent.hpp
===================================================================
--- /pjproject/trunk/pjsip/include/pjsua2/persistent.hpp	(revision 4704)
+++ /pjproject/trunk/pjsip/include/pjsua2/persistent.hpp	(revision 4704)
@@ -0,0 +1,689 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2013 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 __PJSUA2_PERSISTENT_HPP__
+#define __PJSUA2_PERSISTENT_HPP__
+
+/**
+ * @file pjsua2/persistent.hpp
+ * @brief PJSUA2 Persistent Services
+ */
+#include <pjsua2/types.hpp>
+
+#include <string>
+#include <vector>
+
+/** PJSUA2 API is inside pj namespace */
+namespace pj
+{
+
+/**
+ * @defgroup PJSUA2_PERSISTENT Persistent API
+ * @ingroup PJSUA2_Ref
+ * @{
+ * The persistent API provides functionality to read/write data from/to
+ * a document (string or file). The data can be simple data types such
+ * as boolean, number, string, and string arrays, or a user defined object.
+ * Currently the implementation supports reading and writing from/to JSON
+ * document, but the framework allows application to extend the API to
+ * support other document formats.
+ */
+
+using std::string;
+using std::vector;
+
+/* Forward declaration for ContainerNode */
+class ContainerNode;
+
+/**
+ * This is the abstract base class of objects that can be serialized to/from
+ * persistent document.
+ */
+class PersistentObject
+{
+public:
+    /**
+     * Read this object from a container node.
+     *
+     * @param node		Container to read values from.
+     */
+    virtual void readObject(const ContainerNode &node) throw(Error) = 0;
+
+    /**
+     * Write this object to a container node.
+     *
+     * @param node		Container to write values to.
+     */
+    virtual void writeObject(ContainerNode &node) const throw(Error) = 0;
+};
+
+
+/**
+ * This a the abstract base class for a persistent document. A document
+ * is created either by loading from a string or a file, or by constructing
+ * it manually when writing data to it. The document then can be saved
+ * to either string or to a file. A document contains one root ContainerNode
+ * where all data are stored under.
+ *
+ * Document is read and written serially, hence the order of reading must be
+ * the same as the order of writing. The PersistentDocument class provides
+ * API to read and write to the root node, but for more flexible operations
+ * application can use the ContainerNode methods instead. Indeed the read
+ * and write API in PersistentDocument is just a shorthand which calls the
+ * relevant methods in the ContainerNode. As a tip, normally application only
+ * uses the readObject() and writeObject() methods declared here to read/write
+ * top level objects, and use the macros that are explained in ContainerNode
+ * documentation to read/write more detailed data.
+ */
+class PersistentDocument
+{
+public:
+    /**
+     * Virtual destructor
+     */
+    virtual ~PersistentDocument()
+    {}
+
+    /**
+     * Load this document from a file.
+     *
+     * @param filename	The file name.
+     */
+    virtual void   	loadFile(const string &filename) throw(Error) = 0;
+
+    /**
+     * Load this document from string.
+     *
+     * @param input	The string.
+     */
+    virtual void   	loadString(const string &input) throw(Error) = 0;
+
+    /**
+     * Write this document to a file.
+     *
+     * @param filename	The file name.
+     */
+    virtual void   	saveFile(const string &filename) throw(Error) = 0;
+
+    /**
+     * Write this document to string.
+     *
+     * @return		The string document.
+     */
+    virtual string 	saveString() throw(Error) = 0;
+
+    /**
+     * Get the root container node for this document
+     *
+     * @return		The root node.
+     */
+    virtual ContainerNode & getRootContainer() const = 0;
+
+
+    /*
+     * Shorthand functions for reading and writing from/to the root container
+     */
+
+
+    /**
+     * Determine if there is unread element. If yes, then app can use one of
+     * the readXxx() functions to read it.
+     *
+     * @return		True if there is.
+     */
+    bool		hasUnread() const;
+
+    /**
+     * Get the name of the next unread element. It will throw Error if there
+     * is no more element to read.
+     *
+     * @return		The name of the next element .
+     */
+    string		unreadName() const throw(Error);
+
+    /**
+     * Read an integer value from the document and return the value.
+     * This will throw Error if the current element is not a number.
+     * The read position will be advanced to the next element.
+     *
+     * @param name	If specified, then the function will check if the
+     * 			name of the next element matches the specified
+     * 			name and throw Error if it doesn't match.
+     *
+     * @return		The value.
+     */
+    int			readInt(const string &name="") const throw(Error);
+
+    /**
+     * Read a float value from the document and return the value.
+     * This will throw Error if the current element is not a number.
+     * The read position will be advanced to the next element.
+     *
+     * @param name	If specified, then the function will check if the
+     * 			name of the next element matches the specified
+     * 			name and throw Error if it doesn't match.
+     *
+     * @return		The value.
+     */
+    float		readNumber(const string &name="") const throw(Error);
+
+    /**
+     * Read a boolean value from the container and return the value.
+     * This will throw Error if the current element is not a boolean.
+     * The read position will be advanced to the next element.
+     *
+     * @param name	If specified, then the function will check if the
+     * 			name of the next element matches the specified
+     * 			name and throw Error if it doesn't match.
+     *
+     * @return		The value.
+     */
+    bool		readBool(const string &name="") const throw(Error);
+
+    /**
+     * Read a string value from the container and return the value.
+     * This will throw Error if the current element is not a string.
+     * The read position will be advanced to the next element.
+     *
+     * @param name	If specified, then the function will check if the
+     * 			name of the next element matches the specified
+     * 			name and throw Error if it doesn't match.
+     *
+     * @return		The value.
+     */
+    string		readString(const string &name="") const throw(Error);
+
+    /**
+     * Read a string array from the container. This will throw Error
+     * if the current element is not a string array. The read position
+     * will be advanced to the next element.
+     *
+     * @param name	If specified, then the function will check if the
+     * 			name of the next element matches the specified
+     * 			name and throw Error if it doesn't match.
+     *
+     * @return		The value.
+     */
+    StringVector	readStringVector(const string &name="") const
+					 throw(Error);
+
+    /**
+     * Read the specified object from the container. This is equal to
+     * calling PersistentObject.readObject(ContainerNode);
+     *
+     * @param obj	The object to read.
+     */
+    void		readObject(PersistentObject &obj) const throw(Error);
+
+    /**
+     * Read a container from the container. This will throw Error if the
+     * current element is not an object. The read position will be advanced
+     * to the next element.
+     *
+     * @param name	If specified, then the function will check if the
+     * 			name of the next element matches the specified
+     * 			name and throw Error if it doesn't match.
+     *
+     * @return		Container object.
+     */
+    ContainerNode	readContainer(const string &name="") const
+				      throw(Error);
+
+    /**
+     * Read array container from the container. This will throw Error if the
+     * current element is not an array. The read position will be advanced
+     * to the next element.
+     *
+     * @param name	If specified, then the function will check if the
+     * 			name of the next element matches the specified
+     * 			name and throw Error if it doesn't match.
+     *
+     * @return		Container object.
+     */
+    ContainerNode	readArray(const string &name="") const
+				  throw(Error);
+
+    /**
+     * Write a number value to the container.
+     *
+     * @param name	The name for the value in the container.
+     * @param num	The value to be written.
+     */
+    void		writeNumber(const string &name,
+        		            float num) throw(Error);
+
+    /**
+     * Write a number value to the container.
+     *
+     * @param name	The name for the value in the container.
+     * @param num	The value to be written.
+     */
+    void		writeInt(const string &name,
+        		         int num) throw(Error);
+
+    /**
+     * Write a boolean value to the container.
+     *
+     * @param name	The name for the value in the container.
+     * @param value	The value to be written.
+     */
+    void		writeBool(const string &name,
+        		          bool value) throw(Error);
+
+    /**
+     * Write a string value to the container.
+     *
+     * @param name	The name for the value in the container.
+     * @param value	The value to be written.
+     */
+    void		writeString(const string &name,
+        		            const string &value) throw(Error);
+
+    /**
+     * Write string vector to the container.
+     *
+     * @param name	The name for the value in the container.
+     * @param arr	The vector to be written.
+     */
+    void		writeStringVector(const string &name,
+        		                  const StringVector &arr)
+					  throw(Error);
+
+    /**
+     * Write an object to the container. This is equal to calling
+     * PersistentObject.writeObject(ContainerNode);
+     *
+     * @param obj	The object to be written
+     */
+    void		writeObject(const PersistentObject &obj) throw(Error);
+
+    /**
+     * Create and write an empty Object node that can be used as parent
+     * for subsequent write operations.
+     *
+     * @param name	The name for the new container in the container.
+     *
+     * @return		A sub-container.
+     */
+    ContainerNode 	writeNewContainer(const string &name)
+					  throw(Error);
+
+    /**
+     * Create and write an empty array node that can be used as parent
+     * for subsequent write operations.
+     *
+     * @param name	The name for the array.
+     *
+     * @return		A sub-container.
+     */
+    ContainerNode 	writeNewArray(const string &name)
+				      throw(Error);
+};
+
+
+/**
+ * Forward declaration of container_node_op.
+ */
+struct container_node_op;
+
+
+/**
+ * Internal data for ContainerNode. See ContainerNode implementation notes
+ * for more info.
+ */
+struct container_node_internal_data
+{
+    void	*doc;		/**< The document.	*/
+    void	*data1;		/**< Internal data 1	*/
+    void	*data2;		/**< Internal data 2	*/
+};
+
+/**
+ * A container node is a placeholder for storing other data elements, which
+ * could be boolean, number, string, array of strings, or another container.
+ * Each data in the container is basically a name/value pair, with a type
+ * internally associated with it so that written data can be read in the
+ * correct type. Data is read and written serially, hence the order of
+ * reading must be the same as the order of writing.
+ *
+ * Application can read data from it by using the various read methods, and
+ * write data to it using the various write methods. Alternatively, it
+ * may be more convenient to use the provided macros below to read and write
+ * the data, because these macros set the name automatically:
+ *	- NODE_READ_BOOL(node,item)
+ *	- NODE_READ_UNSIGNED(node,item)
+ *	- NODE_READ_INT(node,item)
+ *	- NODE_READ_FLOAT(node,item)
+ *	- NODE_READ_NUM_T(node,type,item)
+ *	- NODE_READ_STRING(node,item)
+ *	- NODE_READ_STRINGV(node,item)
+ *	- NODE_READ_OBJ(node,item)
+ *	- NODE_WRITE_BOOL(node,item)
+ *	- NODE_WRITE_UNSIGNED(node,item)
+ *	- NODE_WRITE_INT(node,item)
+ *	- NODE_WRITE_FLOAT(node,item)
+ *	- NODE_WRITE_NUM_T(node,type,item)
+ *	- NODE_WRITE_STRING(node,item)
+ *	- NODE_WRITE_STRINGV(node,item)
+ *	- NODE_WRITE_OBJ(node,item)
+ *
+ * Implementation notes:
+ *
+ * The ContainerNode class is subclass-able, but not in the usual C++ way.
+ * With the usual C++ inheritance, some methods will be made pure virtual
+ * and must be implemented by the actual class. However, doing so will
+ * require dynamic instantiation of the ContainerNode class, which means
+ * we will need to pass around the class as pointer, for example as the
+ * return value of readContainer() and writeNewContainer() methods. Then
+ * we will need to establish who needs or how to delete these objects, or
+ * use shared pointer mechanism, each of which is considered too inconvenient
+ * or complicated for the purpose.
+ *
+ * So hence we use C style "inheritance", where the methods are declared in
+ * container_node_op and the data in container_node_internal_data structures.
+ * An implementation of ContainerNode class will need to set up these members
+ * with values that makes sense to itself. The methods in container_node_op
+ * contains the pointer to the actual implementation of the operation, which
+ * would be specific according to the format of the document. The methods in
+ * this ContainerNode class are just thin wrappers which call the
+ * implementation in the container_node_op structure.
+ *
+ */
+class ContainerNode
+{
+public:
+    /**
+     * Determine if there is unread element. If yes, then app can use one of
+     * the readXxx() functions to read it.
+     */
+    bool		hasUnread() const;
+
+    /**
+     * Get the name of the next unread element.
+     */
+    string		unreadName() const throw(Error);
+
+    /**
+     * Read an integer value from the document and return the value.
+     * This will throw Error if the current element is not a number.
+     * The read position will be advanced to the next element.
+     *
+     * @param name	If specified, then the function will check if the
+     * 			name of the next element matches the specified
+     * 			name and throw Error if it doesn't match.
+     *
+     * @return		The value.
+     */
+    int			readInt(const string &name="") const throw(Error);
+
+    /**
+     * Read a number value from the document and return the value.
+     * This will throw Error if the current element is not a number.
+     * The read position will be advanced to the next element.
+     *
+     * @param name	If specified, then the function will check if the
+     * 			name of the next element matches the specified
+     * 			name and throw Error if it doesn't match.
+     *
+     * @return		The value.
+     */
+    float		readNumber(const string &name="") const throw(Error);
+
+    /**
+     * Read a boolean value from the container and return the value.
+     * This will throw Error if the current element is not a boolean.
+     * The read position will be advanced to the next element.
+     *
+     * @param name	If specified, then the function will check if the
+     * 			name of the next element matches the specified
+     * 			name and throw Error if it doesn't match.
+     *
+     * @return		The value.
+     */
+    bool		readBool(const string &name="") const throw(Error);
+
+    /**
+     * Read a string value from the container and return the value.
+     * This will throw Error if the current element is not a string.
+     * The read position will be advanced to the next element.
+     *
+     * @param name	If specified, then the function will check if the
+     * 			name of the next element matches the specified
+     * 			name and throw Error if it doesn't match.
+     *
+     * @return		The value.
+     */
+    string		readString(const string &name="") const throw(Error);
+
+    /**
+     * Read a string array from the container. This will throw Error
+     * if the current element is not a string array. The read position
+     * will be advanced to the next element.
+     *
+     * @param name	If specified, then the function will check if the
+     * 			name of the next element matches the specified
+     * 			name and throw Error if it doesn't match.
+     *
+     * @return		The value.
+     */
+    StringVector	readStringVector(const string &name="") const
+					 throw(Error);
+
+    /**
+     * Read the specified object from the container. This is equal to
+     * calling PersistentObject.readObject(ContainerNode);
+     *
+     * @param obj	The object to read.
+     */
+    void		readObject(PersistentObject &obj) const throw(Error);
+
+    /**
+     * Read a container from the container. This will throw Error if the
+     * current element is not a container. The read position will be advanced
+     * to the next element.
+     *
+     * @param name	If specified, then the function will check if the
+     * 			name of the next element matches the specified
+     * 			name and throw Error if it doesn't match.
+     *
+     * @return		Container object.
+     */
+    ContainerNode	readContainer(const string &name="") const
+				      throw(Error);
+
+    /**
+     * Read array container from the container. This will throw Error if the
+     * current element is not an array. The read position will be advanced
+     * to the next element.
+     *
+     * @param name	If specified, then the function will check if the
+     * 			name of the next element matches the specified
+     * 			name and throw Error if it doesn't match.
+     *
+     * @return		Container object.
+     */
+    ContainerNode	readArray(const string &name="") const
+				  throw(Error);
+
+    /**
+     * Write a number value to the container.
+     *
+     * @param name	The name for the value in the container.
+     * @param num	The value to be written.
+     */
+    void		writeNumber(const string &name,
+        		            float num) throw(Error);
+
+    /**
+     * Write a number value to the container.
+     *
+     * @param name	The name for the value in the container.
+     * @param num	The value to be written.
+     */
+    void		writeInt(const string &name,
+        		         int num) throw(Error);
+
+    /**
+     * Write a boolean value to the container.
+     *
+     * @param name	The name for the value in the container.
+     * @param value	The value to be written.
+     */
+    void		writeBool(const string &name,
+        		          bool value) throw(Error);
+
+    /**
+     * Write a string value to the container.
+     *
+     * @param name	The name for the value in the container.
+     * @param value	The value to be written.
+     */
+    void		writeString(const string &name,
+        		            const string &value) throw(Error);
+
+    /**
+     * Write string vector to the container.
+     *
+     * @param name	The name for the value in the container.
+     * @param arr	The vector to be written.
+     */
+    void		writeStringVector(const string &name,
+        		                  const StringVector &arr)
+					  throw(Error);
+
+    /**
+     * Write an object to the container. This is equal to calling
+     * PersistentObject.writeObject(ContainerNode);
+     *
+     * @param obj	The object to be written
+     */
+    void		writeObject(const PersistentObject &obj) throw(Error);
+
+    /**
+     * Create and write an empty Object node that can be used as parent
+     * for subsequent write operations.
+     *
+     * @param name	The name for the new container in the container.
+     *
+     * @return		A sub-container.
+     */
+    ContainerNode 	writeNewContainer(const string &name)
+					  throw(Error);
+
+    /**
+     * Create and write an empty array node that can be used as parent
+     * for subsequent write operations.
+     *
+     * @param name	The name for the array.
+     *
+     * @return		A sub-container.
+     */
+    ContainerNode 	writeNewArray(const string &name)
+				      throw(Error);
+
+public:
+    /* internal data */
+    container_node_op *op;		/**< Method table.	*/
+    container_node_internal_data data;	/**< Internal data	*/
+};
+
+
+/**
+ * Pointer to actual ContainerNode implementation. See ContainerNode
+ * implementation notes for more info.
+ */
+//! @cond Doxygen_Suppress
+struct container_node_op
+{
+    bool		(*hasUnread)(const ContainerNode*);
+    string		(*unreadName)(const ContainerNode*)
+				      throw(Error);
+    float		(*readNumber)(const ContainerNode*,
+				      const string&)
+				      throw(Error);
+    bool		(*readBool)(const ContainerNode*,
+				    const string&)
+				    throw(Error);
+    string		(*readString)(const ContainerNode*,
+				      const string&)
+				      throw(Error);
+    StringVector	(*readStringVector)(const ContainerNode*,
+					    const string&)
+					    throw(Error);
+    ContainerNode	(*readContainer)(const ContainerNode*,
+					 const string &)
+					 throw(Error);
+    ContainerNode	(*readArray)(const ContainerNode*,
+				     const string &)
+				     throw(Error);
+    void		(*writeNumber)(ContainerNode*,
+				       const string &name,
+        		               float num)
+        		               throw(Error);
+    void		(*writeBool)(ContainerNode*,
+				     const string &name,
+        			     bool value)
+        			     throw(Error);
+    void		(*writeString)(ContainerNode*,
+				       const string &name,
+        			       const string &value)
+        			       throw(Error);
+    void		(*writeStringVector)(ContainerNode*,
+					     const string &name,
+        				     const StringVector &value)
+					     throw(Error);
+    ContainerNode 	(*writeNewContainer)(ContainerNode*,
+					     const string &name)
+					     throw(Error);
+    ContainerNode 	(*writeNewArray)(ContainerNode*,
+				         const string &name)
+					 throw(Error);
+};
+
+/*
+ * Convenient macros.
+ */
+#define NODE_READ_BOOL(node,item)	item = node.readBool(#item)
+#define NODE_READ_UNSIGNED(node,item)	item = (unsigned)node.readNumber(#item)
+#define NODE_READ_INT(node,item)	item = (int) node.readNumber(#item)
+#define NODE_READ_FLOAT(node,item)	item = node.readNumber(#item)
+#define NODE_READ_NUM_T(node,T,item)	item = (T)(int)node.readNumber(#item)
+#define NODE_READ_STRING(node,item)	item = node.readString(#item)
+#define NODE_READ_STRINGV(node,item)	item = node.readStringVector(#item)
+#define NODE_READ_OBJ(node,item)	node.readObject(item)
+
+#define NODE_WRITE_BOOL(node,item)	node.writeBool(#item, item)
+#define NODE_WRITE_UNSIGNED(node,item)	node.writeNumber(#item, (float)item)
+#define NODE_WRITE_INT(node,item)	node.writeNumber(#item, (float)item)
+#define NODE_WRITE_NUM_T(node,T,item)	node.writeNumber(#item, (float)item)
+#define NODE_WRITE_FLOAT(node,item)	node.writeNumber(#item, item)
+#define NODE_WRITE_STRING(node,item)	node.writeString(#item, item)
+#define NODE_WRITE_STRINGV(node,item)	node.writeStringVector(#item, item)
+#define NODE_WRITE_OBJ(node,item)	node.writeObject(item)
+
+//! @endcond
+
+/**
+ * @}  PJSUA2
+ */
+
+} // namespace pj
+
+
+
+#endif	/* __PJSUA2_PERSISTENT_HPP__ */
Index: /pjproject/trunk/pjsip/include/pjsua2/presence.hpp
===================================================================
--- /pjproject/trunk/pjsip/include/pjsua2/presence.hpp	(revision 4704)
+++ /pjproject/trunk/pjsip/include/pjsua2/presence.hpp	(revision 4704)
@@ -0,0 +1,296 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2013 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 __PJSUA2_PRESENCE_HPP__
+#define __PJSUA2_PRESENCE_HPP__
+
+/**
+ * @file pjsua2/presence.hpp
+ * @brief PJSUA2 Presence Operations
+ */
+#include <pjsua2/persistent.hpp>
+#include <pjsua2/siptypes.hpp>
+
+/** PJSUA2 API is inside pj namespace */
+namespace pj
+{
+
+/**
+ * @defgroup PJSUA2_PRES Presence
+ * @ingroup PJSUA2_Ref
+ * @{
+ */
+
+using std::string;
+using std::vector;
+
+
+/**
+ * This describes presence status.
+ */
+struct PresenceStatus
+{
+    /**
+     * Buddy's online status.
+     */
+    pjsua_buddy_status	 status;
+
+    /**
+     * Text to describe buddy's online status.
+     */
+    string		 statusText;
+    
+    /**
+     * Activity type.
+     */
+    pjrpid_activity	 activity;
+
+    /**
+     * Optional text describing the person/element.
+     */
+    string		 note;
+
+    /**
+     * Optional RPID ID string.
+     */
+    string		 rpidId;
+
+public:
+    /**
+     * Constructor.
+     */
+    PresenceStatus();
+};
+
+
+/**
+ * This structure describes buddy configuration when adding a buddy to
+ * the buddy list with Buddy::create().
+ */
+struct BuddyConfig : public PersistentObject
+{
+    /**
+     * Buddy URL or name address.
+     */
+    string	 	 uri;
+
+    /**
+     * Specify whether presence subscription should start immediately.
+     */
+    bool	 	 subscribe;
+
+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);
+};
+
+
+/**
+ * This structure describes buddy info, which can be retrieved by via
+ * Buddy::getInfo().
+ */
+struct BuddyInfo
+{
+    /**
+     * The full URI of the buddy, as specified in the configuration.
+     */
+    string		 uri;
+
+    /**
+     * Buddy's Contact, only available when presence subscription has
+     * been established to the buddy.
+     */
+    string		 contact;
+
+    /**
+     * Flag to indicate that we should monitor the presence information for
+     * this buddy (normally yes, unless explicitly disabled).
+     */
+    bool		 presMonitorEnabled;
+
+    /**
+     * If \a presMonitorEnabled is true, this specifies the last state of
+     * the presence subscription. If presence subscription session is currently
+     * active, the value will be PJSIP_EVSUB_STATE_ACTIVE. If presence
+     * subscription request has been rejected, the value will be
+     * PJSIP_EVSUB_STATE_TERMINATED, and the termination reason will be
+     * specified in \a subTermReason.
+     */
+    pjsip_evsub_state	 subState;
+
+    /**
+     * String representation of subscription state.
+     */
+    string	         subStateName;
+
+    /**
+     * Specifies the last presence subscription termination code. This would
+     * return the last status of the SUBSCRIBE request. If the subscription
+     * is terminated with NOTIFY by the server, this value will be set to
+     * 200, and subscription termination reason will be given in the
+     * \a subTermReason field.
+     */
+    pjsip_status_code	 subTermCode;
+
+    /**
+     * Specifies the last presence subscription termination reason. If 
+     * presence subscription is currently active, the value will be empty.
+     */
+    string		 subTermReason;
+
+    /**
+     * Presence status.
+     */
+    PresenceStatus	 presStatus;
+    
+public:
+    /** Import from pjsip structure */
+    void fromPj(const pjsua_buddy_info &pbi);
+};
+
+
+/**
+ * Buddy.
+ */
+class Buddy
+{
+public:
+    /**
+     * Constructor.
+     */
+    Buddy();
+    
+    /**
+     * Destructor. Note that if the Buddy instance is deleted, it will also
+     * delete the corresponding buddy in the PJSUA-LIB.
+     */
+    virtual ~Buddy();
+    
+    /**
+     * Create buddy and register the buddy to PJSUA-LIB.
+     *
+     * @param acc		The account for this buddy.
+     * @param cfg		The buddy config.
+     */
+    void create(Account &acc, const BuddyConfig &cfg) throw(Error);
+    
+    /**
+     * Check if this buddy is valid.
+     *
+     * @return			True if it is.
+     */
+    bool isValid() const;
+
+    /**
+     * Get detailed buddy info.
+     *
+     * @return			Buddy info.
+     */
+    BuddyInfo getInfo() const throw(Error);
+
+    /**
+     * Enable/disable buddy's presence monitoring. Once buddy's presence is
+     * subscribed, application will be informed about buddy's presence status
+     * changed via \a onBuddyState() callback.
+     *
+     * @param subscribe		Specify true to activate presence
+     *				subscription.
+     */
+    void subscribePresence(bool subscribe) throw(Error);
+    
+    /**
+     * Update the presence information for the buddy. Although the library
+     * periodically refreshes the presence subscription for all buddies,
+     * some application may want to refresh the buddy's presence subscription
+     * immediately, and in this case it can use this function to accomplish
+     * this.
+     *
+     * Note that the buddy's presence subscription will only be initiated
+     * if presence monitoring is enabled for the buddy. See
+     * subscribePresence() for more info. Also if presence subscription for
+     * the buddy is already active, this function will not do anything.
+     *
+     * Once the presence subscription is activated successfully for the buddy,
+     * application will be notified about the buddy's presence status in the
+     * \a onBuddyState() callback.
+     */
+     void updatePresence(void) throw(Error);
+     
+    /**
+     * Send instant messaging outside dialog, using this buddy's specified
+     * account for route set and authentication.
+     *
+     * @param prm	Sending instant message parameter.
+     */
+    void sendInstantMessage(const SendInstantMessageParam &prm) throw(Error);
+
+    /**
+     * Send typing indication outside dialog.
+     *
+     * @param prm	Sending instant message parameter.
+     */
+    void sendTypingIndication(const SendTypingIndicationParam &prm)
+	 throw(Error);
+
+public:
+    /*
+     * Callbacks
+     */
+     
+    /**
+     * Notify application when the buddy state has changed.
+     * Application may then query the buddy info to get the details.
+     */
+    virtual void onBuddyState()
+    {}
+     
+private:
+     /**
+      * Buddy ID.
+      */
+     pjsua_buddy_id	 id;
+     
+     /**
+      * Account.
+      */
+     Account		*acc;
+};
+
+
+/** Array of buddies */
+typedef std::vector<Buddy*> BuddyVector;
+
+
+/**
+ * @}  // PJSUA2_PRES
+ */
+
+} // namespace pj
+
+#endif	/* __PJSUA2_PRESENCE_HPP__ */
Index: /pjproject/trunk/pjsip/include/pjsua2/siptypes.hpp
===================================================================
--- /pjproject/trunk/pjsip/include/pjsua2/siptypes.hpp	(revision 4704)
+++ /pjproject/trunk/pjsip/include/pjsua2/siptypes.hpp	(revision 4704)
@@ -0,0 +1,910 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2032 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 __PJSUA2_SIPTYPES_HPP__
+#define __PJSUA2_SIPTYPES_HPP__
+
+/**
+ * @file pjsua2/types.hpp
+ * @brief PJSUA2 Base Types
+ */
+#include <pjsua2/types.hpp>
+#include <pjsua2/persistent.hpp>
+
+#include <string>
+#include <vector>
+
+/** PJSUA2 API is inside pj namespace */
+namespace pj
+{
+
+/**
+ * @defgroup PJSUA2_SIP_Types SIP Types
+ * @ingroup PJSUA2_DS
+ * @{
+ */
+
+/**
+ * Credential information. Credential contains information to authenticate
+ * against a service.
+ */
+struct AuthCredInfo : public PersistentObject
+{
+    /**
+     * The authentication scheme (e.g. "digest").
+     */
+    string	scheme;
+
+    /**
+     * Realm on which this credential is to be used. Use "*" to make
+     * a credential that can be used to authenticate against any challenges.
+     */
+    string	realm;
+
+    /**
+     * Authentication user name.
+     */
+    string	username;
+
+    /**
+     * Type of data that is contained in the "data" field. Use 0 if the data
+     * contains plain text password.
+     */
+    int		dataType;
+
+    /**
+     * The data, which can be a plain text password or a hashed digest.
+     */
+    string	data;
+
+    /*
+     * Digest AKA credential information. Note that when AKA credential
+     * is being used, the \a data field of this pjsip_cred_info is
+     * not used, but it still must be initialized to an empty string.
+     * Please see PJSIP_AUTH_AKA_API for more information.
+     */
+
+    /** Permanent subscriber key. */
+    string	akaK;
+
+    /** Operator variant key. */
+    string	akaOp;
+
+    /** Authentication Management Field	*/
+    string	akaAmf;
+
+public:
+    /** Default constructor */
+    AuthCredInfo();
+
+    /** Construct a credential with the specified parameters */
+    AuthCredInfo(const string &scheme,
+                 const string &realm,
+                 const string &user_name,
+                 const int data_type,
+                 const string data);
+
+    /**
+     * 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);
+};
+
+
+//////////////////////////////////////////////////////////////////////////////
+
+/**
+ * TLS transport settings, to be specified in TransportConfig.
+ */
+struct TlsConfig : public PersistentObject
+{
+    /**
+     * Certificate of Authority (CA) list file.
+     */
+    string		CaListFile;
+
+    /**
+     * Public endpoint certificate file, which will be used as client-
+     * side  certificate for outgoing TLS connection, and server-side
+     * certificate for incoming TLS connection.
+     */
+    string		certFile;
+
+    /**
+     * Optional private key of the endpoint certificate to be used.
+     */
+    string		privKeyFile;
+
+    /**
+     * Password to open private key.
+     */
+    string		password;
+
+    /**
+     * TLS protocol method from pjsip_ssl_method.
+     *
+     * Default is PJSIP_SSL_UNSPECIFIED_METHOD (0), which in turn will
+     * use PJSIP_SSL_DEFAULT_METHOD, which default value is
+     * PJSIP_TLSV1_METHOD.
+     */
+    pjsip_ssl_method	method;
+
+    /**
+     * Ciphers and order preference. The Endpoint::utilSslGetAvailableCiphers()
+     * can be used to check the available ciphers supported by backend.
+     * If the array is empty, then default cipher list of the backend
+     * will be used.
+     */
+    IntVector		ciphers;
+
+    /**
+     * Specifies TLS transport behavior on the server TLS certificate
+     * verification result:
+     * - If \a verifyServer is disabled, TLS transport will just notify
+     *   the application via pjsip_tp_state_callback with state
+     *   PJSIP_TP_STATE_CONNECTED regardless TLS verification result.
+     * - If \a verifyServer is enabled, TLS transport will be shutdown
+     *   and application will be notified with state
+     *   PJSIP_TP_STATE_DISCONNECTED whenever there is any TLS verification
+     *   error, otherwise PJSIP_TP_STATE_CONNECTED will be notified.
+     *
+     * In any cases, application can inspect pjsip_tls_state_info in the
+     * callback to see the verification detail.
+     *
+     * Default value is false.
+     */
+    bool		verifyServer;
+
+    /**
+     * Specifies TLS transport behavior on the client TLS certificate
+     * verification result:
+     * - If \a verifyClient is disabled, TLS transport will just notify
+     *   the application via pjsip_tp_state_callback with state
+     *   PJSIP_TP_STATE_CONNECTED regardless TLS verification result.
+     * - If \a verifyClient is enabled, TLS transport will be shutdown
+     *   and application will be notified with state
+     *   PJSIP_TP_STATE_DISCONNECTED whenever there is any TLS verification
+     *   error, otherwise PJSIP_TP_STATE_CONNECTED will be notified.
+     *
+     * In any cases, application can inspect pjsip_tls_state_info in the
+     * callback to see the verification detail.
+     *
+     * Default value is PJ_FALSE.
+     */
+    bool		verifyClient;
+
+    /**
+     * When acting as server (incoming TLS connections), reject incoming
+     * connection if client doesn't supply a TLS certificate.
+     *
+     * This setting corresponds to SSL_VERIFY_FAIL_IF_NO_PEER_CERT flag.
+     * Default value is PJ_FALSE.
+     */
+    bool		requireClientCert;
+
+    /**
+     * TLS negotiation timeout to be applied for both outgoing and incoming
+     * connection, in milliseconds. If zero, the SSL negotiation doesn't
+     * have a timeout.
+     *
+     * Default: zero
+     */
+    unsigned		msecTimeout;
+
+    /**
+     * QoS traffic type to be set on this transport. When application wants
+     * to apply QoS tagging to the transport, it's preferable to set this
+     * field rather than \a qosParam fields since this is more portable.
+     *
+     * Default value is PJ_QOS_TYPE_BEST_EFFORT.
+     */
+    pj_qos_type 	qosType;
+
+    /**
+     * Set the low level QoS parameters to the transport. This is a lower
+     * level operation than setting the \a qosType field and may not be
+     * supported on all platforms.
+     *
+     * By default all settings in this structure are disabled.
+     */
+    pj_qos_params 	qosParams;
+
+    /**
+     * Specify if the transport should ignore any errors when setting the QoS
+     * traffic type/parameters.
+     *
+     * Default: PJ_TRUE
+     */
+    bool		qosIgnoreError;
+
+public:
+    /** Default constructor initialises with default values */
+    TlsConfig();
+
+    /** Convert to pjsip */
+    pjsip_tls_setting toPj() const;
+
+    /** Convert from pjsip */
+    void fromPj(const pjsip_tls_setting &prm);
+
+    /**
+     * 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);
+};
+
+
+/**
+ * Parameters to create a transport instance.
+ */
+struct TransportConfig : public PersistentObject
+{
+    /**
+     * UDP port number to bind locally. This setting MUST be specified
+     * even when default port is desired. If the value is zero, the
+     * transport will be bound to any available port, and application
+     * can query the port by querying the transport info.
+     */
+    unsigned		port;
+
+    /**
+     * Specify the port range for socket binding, relative to the start
+     * port number specified in \a port. Note that this setting is only
+     * applicable when the start port number is non zero.
+     *
+     * Default value is zero.
+     */
+    unsigned		portRange;
+
+    /**
+     * Optional address to advertise as the address of this transport.
+     * Application can specify any address or hostname for this field,
+     * for example it can point to one of the interface address in the
+     * system, or it can point to the public address of a NAT router
+     * where port mappings have been configured for the application.
+     *
+     * Note: this option can be used for both UDP and TCP as well!
+     */
+    string		publicAddress;
+
+    /**
+     * Optional address where the socket should be bound to. This option
+     * SHOULD only be used to selectively bind the socket to particular
+     * interface (instead of 0.0.0.0), and SHOULD NOT be used to set the
+     * published address of a transport (the public_addr field should be
+     * used for that purpose).
+     *
+     * Note that unlike public_addr field, the address (or hostname) here
+     * MUST correspond to the actual interface address in the host, since
+     * this address will be specified as bind() argument.
+     */
+    string		boundAddress;
+
+    /**
+     * This specifies TLS settings for TLS transport. It is only be used
+     * when this transport config is being used to create a SIP TLS
+     * transport.
+     */
+    TlsConfig		tlsConfig;
+
+    /**
+     * QoS traffic type to be set on this transport. When application wants
+     * to apply QoS tagging to the transport, it's preferable to set this
+     * field rather than \a qosParam fields since this is more portable.
+     *
+     * Default is QoS not set.
+     */
+    pj_qos_type		qosType;
+
+    /**
+     * Set the low level QoS parameters to the transport. This is a lower
+     * level operation than setting the \a qosType field and may not be
+     * supported on all platforms.
+     *
+     * Default is QoS not set.
+     */
+    pj_qos_params	qosParams;
+
+public:
+    /** Default constructor initialises with default values */
+    TransportConfig();
+
+    /** Convert from pjsip */
+    void fromPj(const pjsua_transport_config &prm);
+
+    /** Convert to pjsip */
+    pjsua_transport_config toPj() const;
+
+    /**
+     * 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);
+};
+
+/**
+ * This structure describes transport information returned by
+ * Endpoint::transportGetInfo() function.
+ */
+struct TransportInfo
+{
+    /** PJSUA transport identification. */
+    TransportId	    	    id;
+
+    /** Transport type. */
+    pjsip_transport_type_e  type;
+
+    /** Transport type name. */
+    string		    typeName;
+
+    /** Transport string info/description. */
+    string		    info;
+
+    /** Transport flags (see pjsip_transport_flags_e). */
+    unsigned		    flags;
+
+    /** Local/bound address. */
+    SocketAddress	    localAddress;
+
+    /** Published address (or transport address name). */
+    SocketAddress	    localName;
+
+    /** Current number of objects currently referencing this transport. */
+    unsigned		    usageCount;
+
+public:
+    /** Construct from pjsua_transport_info */
+    void fromPj(const pjsua_transport_info &info);
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+/**
+ * This structure describes an incoming SIP message. It corresponds to the
+ * pjsip_rx_data structure in PJSIP library.
+ */
+struct SipRxData
+{
+    /**
+     * A short info string describing the request, which normally contains
+     * the request method and its CSeq.
+     */
+    string		info;
+
+    /**
+     * The whole message data as a string, containing both the header section
+     * and message body section.
+     */
+    string		wholeMsg;
+
+    /**
+     * Source address of the message.
+     */
+    SocketAddress       srcAddress;
+
+    /**
+     * Pointer to original pjsip_rx_data. Only valid when the struct
+     * is constructed from PJSIP's pjsip_rx_data.
+     */
+    void               *pjRxData;
+
+public:
+    /**
+     * Default constructor.
+     */
+    SipRxData();
+
+    /**
+     * Construct from PJSIP's pjsip_rx_data
+     */
+    void fromPj(pjsip_rx_data &rdata);
+};
+
+/**
+ * This structure describes an outgoing SIP message. It corresponds to the
+ * pjsip_tx_data structure in PJSIP library.
+ */
+struct SipTxData
+{
+    /**
+     * A short info string describing the request, which normally contains
+     * the request method and its CSeq.
+     */
+    string		info;
+    
+    /**
+     * The whole message data as a string, containing both the header section
+     * and message body section.
+     */
+    string		wholeMsg;
+    
+    /**
+     * Destination address of the message.
+     */
+    SocketAddress	dstAddress;
+    
+    /**
+     * Pointer to original pjsip_tx_data. Only valid when the struct
+     * is constructed from PJSIP's pjsip_tx_data.
+     */
+    void               *pjTxData;
+    
+public:
+    /**
+     * Default constructor.
+     */
+    SipTxData();
+
+    /**
+     * Construct from PJSIP's pjsip_tx_data
+     */
+    void fromPj(pjsip_tx_data &tdata);
+};
+
+/**
+ * This structure describes SIP transaction object. It corresponds to the
+ * pjsip_transaction structure in PJSIP library.
+ */
+struct SipTransaction
+{
+    /* Transaction identification. */
+    pjsip_role_e        role;           /**< Role (UAS or UAC)      */
+    string              method;         /**< The method.            */
+    
+    /* State and status. */
+    int			statusCode;     /**< Last status code seen. */
+    string		statusText;     /**< Last reason phrase.    */
+    pjsip_tsx_state_e	state;          /**< State.                 */
+    
+    /* Messages and timer. */
+    SipTxData           lastTx;         /**< Msg kept for retrans.  */
+    
+    /* Original pjsip_transaction. */
+    void               *pjTransaction;  /**< pjsip_transaction.     */
+    
+public:
+    /**
+     * Default constructor.
+     */
+    SipTransaction();
+
+    /**
+     * Construct from PJSIP's pjsip_transaction
+     */
+    void fromPj(pjsip_transaction &tsx);
+};
+
+/**
+ * This structure describes timer event.
+ */
+struct TimerEvent
+{
+    TimerEntry          entry;          /**< The timer entry.           */
+};
+
+/**
+ * This structure describes transaction state changed event.
+ */
+struct TsxStateEvent
+{
+    struct
+    {
+        SipRxData       rdata;          /**< The incoming message.      */
+        SipTxData       tdata;          /**< The outgoing message.      */
+        TimerEntry      timer;          /**< The timer.                 */
+        pj_status_t     status;         /**< Transport error status.    */
+        GenericData     data;           /**< Generic data.              */
+    } src;                              /**< Event source.              */
+    SipTransaction      tsx;            /**< The transaction.           */
+    pjsip_tsx_state_e   prevState;      /**< Previous state.            */
+    pjsip_event_id_e    type;           /**< Type of event source:
+                                         *     - PJSIP_EVENT_TX_MSG
+                                         *     - PJSIP_EVENT_RX_MSG,
+                                         *     - PJSIP_EVENT_TRANSPORT_ERROR
+                                         *     - PJSIP_EVENT_TIMER
+                                         *     - PJSIP_EVENT_USER
+                                         */
+};
+
+/**
+ * This structure describes message transmission event.
+ */
+struct TxMsgEvent
+{
+    SipTxData           tdata;          /**< The transmit data buffer.  */
+};
+
+/**
+ * This structure describes transmission error event.
+ */
+struct TxErrorEvent
+{
+    SipTxData           tdata;          /**< The transmit data.         */
+    SipTransaction      tsx;            /**< The transaction.           */
+};
+
+/**
+ * This structure describes message arrival event.
+ */
+struct RxMsgEvent
+{
+    SipRxData           rdata;          /**< The receive data buffer.   */
+};
+
+/**
+ * This structure describes user event.
+ */
+struct UserEvent
+{
+    GenericData         user1;          /**< User data 1.               */
+    GenericData         user2;          /**< User data 2.               */
+    GenericData         user3;          /**< User data 3.               */
+    GenericData         user4;          /**< User data 4.               */
+};
+
+/**
+ * This structure describe event descriptor to fully identify a SIP event. It
+ * corresponds to the pjsip_event structure in PJSIP library.
+ */
+struct SipEvent
+{
+    /**
+     * The event type, can be any value of \b pjsip_event_id_e.
+     */
+    pjsip_event_id_e    type;
+    
+    /**
+     * The event body, which fields depends on the event type.
+     */
+    struct
+    {
+        /**
+         * Timer event.
+         */
+        TimerEvent      timer;
+        
+        /**
+         * Transaction state has changed event.
+         */
+        TsxStateEvent   tsxState;
+        
+        /**
+         * Message transmission event.
+         */
+        TxMsgEvent      txMsg;
+        
+        /**
+         * Transmission error event.
+         */
+        TxErrorEvent    txError;
+        
+        /**
+         * Message arrival event.
+         */
+        RxMsgEvent      rxMsg;
+        
+        /**
+         * User event.
+         */
+        UserEvent       user;
+        
+    } body;
+    
+    /**
+     * Pointer to its original pjsip_event. Only valid when the struct is
+     * constructed from PJSIP's pjsip_event.
+     */
+    void               *pjEvent;
+    
+public:
+    /**
+     * Default constructor.
+     */
+    SipEvent();
+
+    /**
+     * Construct from PJSIP's pjsip_event
+     */
+    void fromPj(const pjsip_event &ev);
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+/**
+ * SIP media type containing type and subtype. For example, for
+ * "application/sdp", the type is "application" and the subtype is "sdp".
+ */
+struct SipMediaType
+{
+    /** Media type. */
+    string		type;
+
+    /** Media subtype. */
+    string		subType;
+
+public:
+    /**
+     * Construct from PJSIP's pjsip_media_type
+     */
+    void fromPj(const pjsip_media_type &prm);
+
+    /**
+     * Convert to PJSIP's pjsip_media_type.
+     */
+    pjsip_media_type toPj() const;
+};
+
+/**
+ * Simple SIP header.
+ */
+struct SipHeader
+{
+    /**
+     * Header name.
+     */
+    string		hName;
+
+    /**
+     * Header value.
+     */
+    string		hValue;
+
+public:
+    /**
+     * Initiaize from PJSIP header.
+     */
+    void fromPj(const pjsip_hdr *) throw(Error);
+
+    /**
+     * Convert to PJSIP header.
+     */
+    pjsip_generic_string_hdr &toPj() const;
+
+private:
+    /** Interal buffer for conversion to PJSIP header */
+    mutable pjsip_generic_string_hdr	pjHdr;
+};
+
+
+/** Array of strings */
+typedef std::vector<SipHeader> SipHeaderVector;
+
+/**
+ * This describes each multipart part.
+ */
+struct SipMultipartPart
+{
+    /**
+     * Optional headers to be put in this multipart part.
+     */
+    SipHeaderVector	headers;
+
+    /**
+     * The MIME type of the body part of this multipart part.
+     */
+    SipMediaType	contentType;
+
+    /**
+     * The body part of tthis multipart part.
+     */
+    string		body;
+
+public:
+    /**
+     * Initiaize from PJSIP's pjsip_multipart_part.
+     */
+    void fromPj(const pjsip_multipart_part &prm) throw(Error);
+
+    /**
+     * Convert to PJSIP's pjsip_multipart_part.
+     */
+    pjsip_multipart_part& toPj() const;
+
+private:
+    /** Interal buffer for conversion to PJSIP pjsip_multipart_part */
+    mutable pjsip_multipart_part	pjMpp;
+    mutable pjsip_msg_body		pjMsgBody;
+};
+
+/** Array of multipart parts */
+typedef std::vector<SipMultipartPart> SipMultipartPartVector;
+
+/**
+ * Additional options when sending outgoing SIP message. This corresponds to
+ * pjsua_msg_data structure in PJSIP library.
+ */
+struct SipTxOption
+{
+    /**
+     * Optional remote target URI (i.e. Target header). If empty (""), the
+     * target will be set to the remote URI (To header). At the moment this
+     * field is only used when sending initial INVITE and MESSAGE requests.
+     */
+    string                  targetUri;
+
+    /**
+     * Additional message headers to be included in the outgoing message.
+     */
+    SipHeaderVector         headers;
+
+    /**
+     * MIME type of the message body, if application specifies the messageBody
+     * in this structure.
+     */
+    string                  contentType;
+
+    /**
+     * Optional message body to be added to the message, only when the
+     * message doesn't have a body.
+     */
+    string                  msgBody;
+
+    /**
+     * Content type of the multipart body. If application wants to send
+     * multipart message bodies, it puts the parts in multipartParts and set
+     * the content type in multipartContentType. If the message already
+     * contains a body, the body will be added to the multipart bodies.
+     */
+    SipMediaType            multipartContentType;
+
+    /**
+     * Array of multipart parts. If application wants to send multipart
+     * message bodies, it puts the parts in \a parts and set the content
+     * type in \a multipart_ctype. If the message already contains a body,
+     * the body will be added to the multipart bodies.
+     */
+    SipMultipartPartVector  multipartParts;
+
+public:
+    /**
+     * Check if the options are empty. If the options are set with empty
+     * values, there will be no additional information sent with outgoing
+     * SIP message.
+     *
+     * @return              True if the options are empty.
+     */
+    bool isEmpty() const;
+    
+    /**
+     * Initiaize from PJSUA's pjsua_msg_data.
+     */
+    void fromPj(const pjsua_msg_data &prm) throw(Error);
+
+    /**
+     * Convert to PJSUA's pjsua_msg_data.
+     */
+    void toPj(pjsua_msg_data &msg_data) const;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+/**
+ * This structure contains parameters for sending instance message methods,
+ * e.g: Buddy::sendInstantMessage(), Call:sendInstantMessage().
+ */
+struct SendInstantMessageParam
+{
+    /**
+     * MIME type. Default is "text/plain".
+     */
+    string      contentType;
+    
+    /**
+     * The message content.
+     */
+    string      content;
+    
+    /**
+     * List of headers etc to be included in outgoing request.
+     */
+    SipTxOption txOption;
+    
+    /**
+     * User data, which will be given back when the IM callback is called.
+     */
+    Token       userData;
+    
+public:
+    /**
+     * Default constructor initializes with zero/empty values.
+     */
+    SendInstantMessageParam();
+};
+
+
+/**
+ * This structure contains parameters for sending typing indication methods,
+ * e.g: Buddy::sendTypingIndication(), Call:sendTypingIndication().
+ */
+struct SendTypingIndicationParam
+{
+    /**
+     * True to indicate to remote that local person is currently typing an IM.
+     */
+    bool         isTyping;
+    
+    /**
+     * List of headers etc to be included in outgoing request.
+     */
+    SipTxOption  txOption;
+    
+public:
+    /**
+     * Default constructor initializes with zero/empty values.
+     */
+    SendTypingIndicationParam();
+};
+
+
+/* Utilities */
+#ifndef SWIG
+//! @cond Doxygen_Suppress
+void readIntVector( ContainerNode &node,
+                    const string &array_name,
+                    IntVector &v) throw(Error);
+void writeIntVector(ContainerNode &node,
+                    const string &array_name,
+                    const IntVector &v) throw(Error);
+void readQosParams( ContainerNode &node,
+                    pj_qos_params &qos) throw(Error);
+void writeQosParams( ContainerNode &node,
+                     const pj_qos_params &qos) throw(Error);
+void readSipHeaders( const ContainerNode &node,
+                     const string &array_name,
+                     SipHeaderVector &headers) throw(Error);
+void writeSipHeaders(ContainerNode &node,
+                     const string &array_name,
+                     const SipHeaderVector &headers) throw(Error);
+//! @endcond
+#endif // SWIG
+
+/**
+ * @}  PJSUA2
+ */
+
+} // namespace pj
+
+
+
+#endif	/* __PJSUA2_SIPTYPES_HPP__ */
Index: /pjproject/trunk/pjsip/include/pjsua2/types.hpp
===================================================================
--- /pjproject/trunk/pjsip/include/pjsua2/types.hpp	(revision 4704)
+++ /pjproject/trunk/pjsip/include/pjsua2/types.hpp	(revision 4704)
@@ -0,0 +1,266 @@
+/* $Id$ */
+/* 
+ * Copyright (C) 2013 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 __PJSUA2_TYPES_HPP__
+#define __PJSUA2_TYPES_HPP__
+
+#ifdef _MSC_VER
+#   pragma warning( disable : 4290 ) // exception spec ignored
+#   pragma warning( disable : 4512 ) // can't generate assignment op
+#endif
+
+/**
+ * @file pjsua2/types.hpp
+ * @brief PJSUA2 Base Types
+ */
+#include <pjsua2/config.hpp>
+
+#include <string>
+#include <vector>
+
+/** PJSUA2 API is inside pj namespace */
+namespace pj
+{
+
+/**
+ * @defgroup PJSUA2_TYPES General Data Structure
+ * @ingroup PJSUA2_DS
+ * @{
+ */
+
+using std::string;
+using std::vector;
+
+/** Array of strings */
+typedef std::vector<std::string> StringVector;
+
+/** Array of integers */
+typedef std::vector<int> IntVector;
+
+/**
+ * Type of token, i.e. arbitrary application user data
+ */
+typedef void *Token;
+
+/**
+ * Socket address, encoded as string. The socket address contains host
+ * and port number in "host[:port]" format. The host part may contain
+ * hostname, domain name, IPv4 or IPv6 address. For IPv6 address, the
+ * address will be enclosed with square brackets, e.g. "[::1]:5060".
+ */
+typedef string SocketAddress;
+
+/**
+ * Transport ID is an integer.
+ */
+typedef int TransportId;
+
+/**
+ * Transport handle, corresponds to pjsip_transport instance.
+ */
+typedef void *TransportHandle;
+
+/**
+ * Timer entry, corresponds to pj_timer_entry
+ */
+typedef void *TimerEntry;
+
+/**
+ * Generic data
+ */
+typedef void *GenericData;
+
+/*
+ * Forward declaration of Account and Call to be used
+ * by Endpoint.
+ */
+class Account;
+class Call;
+
+
+/**
+ * Constants
+ */
+enum
+{
+    /** Invalid ID, equal to PJSUA_INVALID_ID */
+    INVALID_ID	= -1,
+
+    /** Success, equal to PJ_SUCCESS */
+    SUCCESS = 0
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+/**
+ * This structure contains information about an error that is thrown
+ * as an exception.
+ */
+struct Error
+{
+    /** The error code. */
+    pj_status_t	status;
+
+    /** The PJSUA API operation that throws the error. */
+    string	title;
+
+    /** The error message */
+    string	reason;
+
+    /** The PJSUA source file that throws the error */
+    string	srcFile;
+
+    /** The line number of PJSUA source file that throws the error */
+    int		srcLine;
+
+    /** Build error string. */
+    string	info(bool multi_line=false) const;
+
+    /** Default constructor */
+    Error();
+
+    /**
+     * Construct an Error instance from the specified parameters. If
+     * \a prm_reason is empty, it will be filled with the error description
+     *  for the status code.
+     */
+    Error(pj_status_t prm_status,
+          const string &prm_title,
+          const string &prm_reason,
+          const string &prm_src_file,
+          int prm_src_line);
+};
+
+
+/*
+ * Error utilities.
+ */
+#if PJSUA2_ERROR_HAS_EXTRA_INFO
+#   define PJSUA2_RAISE_ERROR(status)		\
+	PJSUA2_RAISE_ERROR2(status, __FUNCTION__)
+
+#   define PJSUA2_RAISE_ERROR2(status,op)	\
+	PJSUA2_RAISE_ERROR3(status, op, string())
+
+#   define PJSUA2_RAISE_ERROR3(status,op,txt)	\
+	do { \
+	    Error err_ = Error(status, op, txt, __FILE__, __LINE__); \
+	    PJ_LOG(1,(THIS_FILE, "%s", err_.info().c_str())); \
+	    throw err_; \
+	} while (0)
+
+#else
+    /** Raise Error exception */
+#   define PJSUA2_RAISE_ERROR(status)		\
+	PJSUA2_RAISE_ERROR2(status, string())
+
+/** Raise Error exception */
+#   define PJSUA2_RAISE_ERROR2(status,op)	\
+	PJSUA2_RAISE_ERROR3(status, op, string())
+
+/** Raise Error exception */
+#   define PJSUA2_RAISE_ERROR3(status,op,txt)	\
+	do { \
+	    Error err_ = Error(status, op, txt, string(), 0); \
+	    PJ_LOG(1,(THIS_FILE, "%s", err_.info().c_str())); \
+	    throw err_; \
+	} while (0)
+
+#endif
+
+/** Raise Error exception if the expression fails */
+#define PJSUA2_CHECK_RAISE_ERROR2(status, op)	\
+	do { \
+	    if (status != PJ_SUCCESS) { \
+		PJSUA2_RAISE_ERROR2(status, op); \
+	    } \
+	} while (0)
+
+/** Raise Error exception if the status fails */
+#define PJSUA2_CHECK_RAISE_ERROR(status)	\
+	PJSUA2_CHECK_RAISE_ERROR2(status, "")
+
+/** Raise Error exception if the expression fails */
+#define PJSUA2_CHECK_EXPR(expr)			\
+	do { \
+	    pj_status_t the_status = expr; 	\
+	    PJSUA2_CHECK_RAISE_ERROR2(the_status, #expr); \
+	} while (0)
+
+//////////////////////////////////////////////////////////////////////////////
+/**
+ * Version information.
+ */
+struct Version
+{
+    /** Major number */
+    int		major;
+
+    /** Minor number */
+    int		minor;
+
+    /** Additional revision number */
+    int		rev;
+
+    /** Version suffix (e.g. "-svn") */
+    string	suffix;
+
+    /** The full version info (e.g. "2.1.0-svn") */
+    string	full;
+
+    /**
+     * PJLIB version number as three bytes with the following format:
+     * 0xMMIIRR00, where MM: major number, II: minor number, RR: revision
+     * number, 00: always zero for now.
+     */
+    unsigned	numeric;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Representation of time value.
+ */
+struct TimeValue
+{
+    /**
+     * The seconds part of the time.
+     */
+    long sec;
+    
+    /**
+     * The miliseconds fraction of the time.
+     */
+    long msec;
+    
+public:
+    /**
+     * Convert from pjsip
+     */
+    void fromPj(const pj_time_val &prm);
+};
+
+/**
+ * @}  PJSUA2
+ */
+
+} // namespace pj
+
+
+
+#endif	/* __PJSUA2_TYPES_HPP__ */
Index: /pjproject/trunk/pjsip/src/pjsip/sip_endpoint.c
===================================================================
--- /pjproject/trunk/pjsip/src/pjsip/sip_endpoint.c	(revision 4703)
+++ /pjproject/trunk/pjsip/src/pjsip/sip_endpoint.c	(revision 4704)
@@ -924,4 +924,6 @@
     pj_bool_t handled = PJ_FALSE;
 
+    PJ_UNUSED_ARG(msg);
+
     if (status != PJ_SUCCESS) {
 	char info[30];
Index: /pjproject/trunk/pjsip/src/pjsua-lib/pjsua_aud.c
===================================================================
--- /pjproject/trunk/pjsip/src/pjsua-lib/pjsua_aud.c	(revision 4703)
+++ /pjproject/trunk/pjsip/src/pjsua-lib/pjsua_aud.c	(revision 4704)
@@ -742,8 +742,11 @@
     info->slot_id = id;
     info->name = cinfo.name;
+    pjmedia_format_copy(&info->format, &cinfo.format);
     info->clock_rate = cinfo.clock_rate;
     info->channel_count = cinfo.channel_count;
     info->samples_per_frame = cinfo.samples_per_frame;
     info->bits_per_sample = cinfo.bits_per_sample;
+    info->tx_level_adj = ((float)cinfo.tx_adj_level) / 128 + 1;
+    info->rx_level_adj = ((float)cinfo.rx_adj_level) / 128 + 1;
 
     /* Build array of listeners */
Index: /pjproject/trunk/pjsip/src/pjsua-lib/pjsua_core.c
===================================================================
--- /pjproject/trunk/pjsip/src/pjsua-lib/pjsua_core.c	(revision 4703)
+++ /pjproject/trunk/pjsip/src/pjsua-lib/pjsua_core.c	(revision 4704)
@@ -700,4 +700,48 @@
 }
 
+PJ_DEF(pj_status_t) pjsua_register_worker_thread(const char *name)
+{
+    pj_thread_desc desc;
+    pj_thread_t *thread;
+    pj_status_t status;
+
+    if (pjsua_var.thread_quit_flag)
+	return PJ_EGONE;
+
+    status = pj_thread_register(NULL, desc, &thread);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    if (name)
+	PJ_LOG(4,(THIS_FILE, "Worker thread %s started", name));
+
+    worker_thread(NULL);
+
+    if (name)
+	PJ_LOG(4,(THIS_FILE, "Worker thread %s stopped", name));
+
+    return PJ_SUCCESS;
+}
+
+PJ_DEF(void) pjsua_stop_worker_threads(void)
+{
+    unsigned i;
+
+    pjsua_var.thread_quit_flag = 1;
+
+    /* Wait worker threads to quit: */
+    for (i=0; i<(int)pjsua_var.ua_cfg.thread_cnt; ++i) {
+    	if (pjsua_var.thread[i]) {
+    	    pj_status_t status;
+    	    status = pj_thread_join(pjsua_var.thread[i]);
+    	    if (status != PJ_SUCCESS) {
+    		PJ_PERROR(4,(THIS_FILE, status, "Error joining worker thread"));
+    		pj_thread_sleep(1000);
+    	    }
+    	    pj_thread_destroy(pjsua_var.thread[i]);
+    	    pjsua_var.thread[i] = NULL;
+    	}
+    }
+}
 
 /* Init random seed */
@@ -1479,19 +1523,5 @@
 
     /* Signal threads to quit: */
-    pjsua_var.thread_quit_flag = 1;
-
-    /* Wait worker threads to quit: */
-    for (i=0; i<(int)pjsua_var.ua_cfg.thread_cnt; ++i) {
-	if (pjsua_var.thread[i]) {
-	    pj_status_t status;
-	    status = pj_thread_join(pjsua_var.thread[i]);
-	    if (status != PJ_SUCCESS) {
-		PJ_PERROR(4,(THIS_FILE, status, "Error joining worker thread"));
-		pj_thread_sleep(1000);
-	    }
-	    pj_thread_destroy(pjsua_var.thread[i]);
-	    pjsua_var.thread[i] = NULL;
-	}
-    }
+    pjsua_stop_worker_threads();
     
     if (pjsua_var.endpt) {
Index: /pjproject/trunk/pjsip/src/pjsua2-test/main.cpp
===================================================================
--- /pjproject/trunk/pjsip/src/pjsua2-test/main.cpp	(revision 4704)
+++ /pjproject/trunk/pjsip/src/pjsua2-test/main.cpp	(revision 4704)
@@ -0,0 +1,38 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2012 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 <pjsua2/endpoint.hpp>
+
+using namespace pj;
+
+int main(int argc, char *argv[])
+{
+    Endpoint &ep = Endpoint::instance();
+
+    EpConfig epCfg;
+
+    epCfg.uaConfig.userAgent = "pjsua++-test";
+
+    ep.libCreate();
+    ep.libInit(epCfg);
+    ep.libStart();
+    ep.libDestroy();
+
+    return 0;
+}
+
Index: /pjproject/trunk/pjsip/src/pjsua2/account.cpp
===================================================================
--- /pjproject/trunk/pjsip/src/pjsua2/account.cpp	(revision 4704)
+++ /pjproject/trunk/pjsip/src/pjsua2/account.cpp	(revision 4704)
@@ -0,0 +1,799 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2013 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 <pjsua2/account.hpp>
+#include <pjsua2/endpoint.hpp>
+#include <pjsua2/presence.hpp>
+#include <pj/ctype.h>
+#include "util.hpp"
+
+using namespace pj;
+using namespace std;
+
+#define THIS_FILE		"account.cpp"
+
+///////////////////////////////////////////////////////////////////////////////
+
+void AccountRegConfig::readObject(const ContainerNode &node) throw(Error)
+{
+    ContainerNode this_node = node.readContainer("AccountRegConfig");
+
+    NODE_READ_STRING	(this_node, registrarUri);
+    NODE_READ_BOOL	(this_node, registerOnAdd);
+    NODE_READ_UNSIGNED	(this_node, timeoutSec);
+    NODE_READ_UNSIGNED	(this_node, retryIntervalSec);
+    NODE_READ_UNSIGNED	(this_node, firstRetryIntervalSec);
+    NODE_READ_UNSIGNED	(this_node, delayBeforeRefreshSec);
+    NODE_READ_BOOL	(this_node, dropCallsOnFail);
+    NODE_READ_UNSIGNED	(this_node, unregWaitSec);
+    NODE_READ_UNSIGNED	(this_node, proxyUse);
+
+    readSipHeaders(this_node, "headers", headers);
+}
+
+void AccountRegConfig::writeObject(ContainerNode &node) const throw(Error)
+{
+    ContainerNode this_node = node.writeNewContainer("AccountRegConfig");
+
+    NODE_WRITE_STRING	(this_node, registrarUri);
+    NODE_WRITE_BOOL	(this_node, registerOnAdd);
+    NODE_WRITE_UNSIGNED	(this_node, timeoutSec);
+    NODE_WRITE_UNSIGNED	(this_node, retryIntervalSec);
+    NODE_WRITE_UNSIGNED	(this_node, firstRetryIntervalSec);
+    NODE_WRITE_UNSIGNED	(this_node, delayBeforeRefreshSec);
+    NODE_WRITE_BOOL	(this_node, dropCallsOnFail);
+    NODE_WRITE_UNSIGNED	(this_node, unregWaitSec);
+    NODE_WRITE_UNSIGNED	(this_node, proxyUse);
+
+    writeSipHeaders(this_node, "headers", headers);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void AccountSipConfig::readObject(const ContainerNode &node) throw(Error)
+{
+    ContainerNode this_node = node.readContainer("AccountSipConfig");
+
+    NODE_READ_STRINGV	(this_node, proxies);
+    NODE_READ_STRING	(this_node, contactForced);
+    NODE_READ_STRING	(this_node, contactParams);
+    NODE_READ_STRING	(this_node, contactUriParams);
+    NODE_READ_BOOL	(this_node, authInitialEmpty);
+    NODE_READ_STRING	(this_node, authInitialAlgorithm);
+    NODE_READ_INT	(this_node, transportId);
+
+    ContainerNode creds_node = this_node.readArray("authCreds");
+    authCreds.resize(0);
+    while (creds_node.hasUnread()) {
+	AuthCredInfo cred;
+	cred.readObject(creds_node);
+	authCreds.push_back(cred);
+    }
+}
+
+void AccountSipConfig::writeObject(ContainerNode &node) const throw(Error)
+{
+    ContainerNode this_node = node.writeNewContainer("AccountSipConfig");
+
+    NODE_WRITE_STRINGV	(this_node, proxies);
+    NODE_WRITE_STRING	(this_node, contactForced);
+    NODE_WRITE_STRING	(this_node, contactParams);
+    NODE_WRITE_STRING	(this_node, contactUriParams);
+    NODE_WRITE_BOOL	(this_node, authInitialEmpty);
+    NODE_WRITE_STRING	(this_node, authInitialAlgorithm);
+    NODE_WRITE_INT	(this_node, transportId);
+
+    ContainerNode creds_node = this_node.writeNewArray("authCreds");
+    for (unsigned i=0; i<authCreds.size(); ++i) {
+	authCreds[i].writeObject(creds_node);
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void AccountCallConfig::readObject(const ContainerNode &node) throw(Error)
+{
+    ContainerNode this_node = node.readContainer("AccountCallConfig");
+
+    NODE_READ_NUM_T   ( this_node, pjsua_call_hold_type, holdType);
+    NODE_READ_NUM_T   ( this_node, pjsua_100rel_use, prackUse);
+    NODE_READ_NUM_T   ( this_node, pjsua_sip_timer_use, timerUse);
+    NODE_READ_UNSIGNED( this_node, timerMinSESec);
+    NODE_READ_UNSIGNED( this_node, timerSessExpiresSec);
+}
+
+void AccountCallConfig::writeObject(ContainerNode &node) const throw(Error)
+{
+    ContainerNode this_node = node.writeNewContainer("AccountCallConfig");
+
+    NODE_WRITE_NUM_T   ( this_node, pjsua_call_hold_type, holdType);
+    NODE_WRITE_NUM_T   ( this_node, pjsua_100rel_use, prackUse);
+    NODE_WRITE_NUM_T   ( this_node, pjsua_sip_timer_use, timerUse);
+    NODE_WRITE_UNSIGNED( this_node, timerMinSESec);
+    NODE_WRITE_UNSIGNED( this_node, timerSessExpiresSec);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void AccountPresConfig::readObject(const ContainerNode &node) throw(Error)
+{
+    ContainerNode this_node = node.readContainer("AccountPresConfig");
+
+    NODE_READ_BOOL    ( this_node, publishEnabled);
+    NODE_READ_BOOL    ( this_node, publishQueue);
+    NODE_READ_UNSIGNED( this_node, publishShutdownWaitMsec);
+    NODE_READ_STRING  ( this_node, pidfTupleId);
+
+    readSipHeaders(this_node, "headers", headers);
+}
+
+void AccountPresConfig::writeObject(ContainerNode &node) const throw(Error)
+{
+    ContainerNode this_node = node.writeNewContainer("AccountPresConfig");
+
+    NODE_WRITE_BOOL    ( this_node, publishEnabled);
+    NODE_WRITE_BOOL    ( this_node, publishQueue);
+    NODE_WRITE_UNSIGNED( this_node, publishShutdownWaitMsec);
+    NODE_WRITE_STRING  ( this_node, pidfTupleId);
+
+    writeSipHeaders(this_node, "headers", headers);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void AccountMwiConfig::readObject(const ContainerNode &node) throw(Error)
+{
+    ContainerNode this_node = node.readContainer("AccountMwiConfig");
+
+    NODE_READ_BOOL    ( this_node, enabled);
+    NODE_READ_UNSIGNED( this_node, expirationSec);
+}
+
+void AccountMwiConfig::writeObject(ContainerNode &node) const throw(Error)
+{
+    ContainerNode this_node = node.writeNewContainer("AccountMwiConfig");
+
+    NODE_WRITE_BOOL    ( this_node, enabled);
+    NODE_WRITE_UNSIGNED( this_node, expirationSec);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void AccountNatConfig::readObject(const ContainerNode &node) throw(Error)
+{
+    ContainerNode this_node = node.readContainer("AccountNatConfig");
+
+    NODE_READ_NUM_T   ( this_node, pjsua_stun_use, sipStunUse);
+    NODE_READ_NUM_T   ( this_node, pjsua_stun_use, mediaStunUse);
+    NODE_READ_BOOL    ( this_node, iceEnabled);
+    NODE_READ_INT     ( this_node, iceMaxHostCands);
+    NODE_READ_BOOL    ( this_node, iceAggressiveNomination);
+    NODE_READ_UNSIGNED( this_node, iceNominatedCheckDelayMsec);
+    NODE_READ_INT     ( this_node, iceWaitNominationTimeoutMsec);
+    NODE_READ_BOOL    ( this_node, iceNoRtcp);
+    NODE_READ_BOOL    ( this_node, iceAlwaysUpdate);
+    NODE_READ_BOOL    ( this_node, turnEnabled);
+    NODE_READ_STRING  ( this_node, turnServer);
+    NODE_READ_NUM_T   ( this_node, pj_turn_tp_type, turnConnType);
+    NODE_READ_STRING  ( this_node, turnUserName);
+    NODE_READ_INT     ( this_node, turnPasswordType);
+    NODE_READ_STRING  ( this_node, turnPassword);
+    NODE_READ_INT     ( this_node, contactRewriteUse);
+    NODE_READ_INT     ( this_node, contactRewriteMethod);
+    NODE_READ_INT     ( this_node, viaRewriteUse);
+    NODE_READ_INT     ( this_node, sdpNatRewriteUse);
+    NODE_READ_INT     ( this_node, sipOutboundUse);
+    NODE_READ_STRING  ( this_node, sipOutboundInstanceId);
+    NODE_READ_STRING  ( this_node, sipOutboundRegId);
+    NODE_READ_UNSIGNED( this_node, udpKaIntervalSec);
+    NODE_READ_STRING  ( this_node, udpKaData);
+}
+
+void AccountNatConfig::writeObject(ContainerNode &node) const throw(Error)
+{
+    ContainerNode this_node = node.writeNewContainer("AccountNatConfig");
+
+    NODE_WRITE_NUM_T   ( this_node, pjsua_stun_use, sipStunUse);
+    NODE_WRITE_NUM_T   ( this_node, pjsua_stun_use, mediaStunUse);
+    NODE_WRITE_BOOL    ( this_node, iceEnabled);
+    NODE_WRITE_INT     ( this_node, iceMaxHostCands);
+    NODE_WRITE_BOOL    ( this_node, iceAggressiveNomination);
+    NODE_WRITE_UNSIGNED( this_node, iceNominatedCheckDelayMsec);
+    NODE_WRITE_INT     ( this_node, iceWaitNominationTimeoutMsec);
+    NODE_WRITE_BOOL    ( this_node, iceNoRtcp);
+    NODE_WRITE_BOOL    ( this_node, iceAlwaysUpdate);
+    NODE_WRITE_BOOL    ( this_node, turnEnabled);
+    NODE_WRITE_STRING  ( this_node, turnServer);
+    NODE_WRITE_NUM_T   ( this_node, pj_turn_tp_type, turnConnType);
+    NODE_WRITE_STRING  ( this_node, turnUserName);
+    NODE_WRITE_INT     ( this_node, turnPasswordType);
+    NODE_WRITE_STRING  ( this_node, turnPassword);
+    NODE_WRITE_INT     ( this_node, contactRewriteUse);
+    NODE_WRITE_INT     ( this_node, contactRewriteMethod);
+    NODE_WRITE_INT     ( this_node, viaRewriteUse);
+    NODE_WRITE_INT     ( this_node, sdpNatRewriteUse);
+    NODE_WRITE_INT     ( this_node, sipOutboundUse);
+    NODE_WRITE_STRING  ( this_node, sipOutboundInstanceId);
+    NODE_WRITE_STRING  ( this_node, sipOutboundRegId);
+    NODE_WRITE_UNSIGNED( this_node, udpKaIntervalSec);
+    NODE_WRITE_STRING  ( this_node, udpKaData);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void AccountMediaConfig::readObject(const ContainerNode &node) throw(Error)
+{
+    ContainerNode this_node = node.readContainer("AccountMediaConfig");
+
+    NODE_READ_BOOL    ( this_node, lockCodecEnabled);
+    NODE_READ_BOOL    ( this_node, streamKaEnabled);
+    NODE_READ_NUM_T   ( this_node, pjmedia_srtp_use, srtpUse);
+    NODE_READ_INT     ( this_node, srtpSecureSignaling);
+    NODE_READ_NUM_T   ( this_node, pjsua_ipv6_use, ipv6Use);
+    NODE_READ_OBJ     ( this_node, transportConfig);
+}
+
+void AccountMediaConfig::writeObject(ContainerNode &node) const throw(Error)
+{
+    ContainerNode this_node = node.writeNewContainer("AccountMediaConfig");
+
+    NODE_WRITE_BOOL    ( this_node, lockCodecEnabled);
+    NODE_WRITE_BOOL    ( this_node, streamKaEnabled);
+    NODE_WRITE_NUM_T   ( this_node, pjmedia_srtp_use, srtpUse);
+    NODE_WRITE_INT     ( this_node, srtpSecureSignaling);
+    NODE_WRITE_NUM_T   ( this_node, pjsua_ipv6_use, ipv6Use);
+    NODE_WRITE_OBJ     ( this_node, transportConfig);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void AccountVideoConfig::readObject(const ContainerNode &node) throw(Error)
+{
+    ContainerNode this_node = node.readContainer("AccountVideoConfig");
+
+    NODE_READ_BOOL    ( this_node, autoShowIncoming);
+    NODE_READ_BOOL    ( this_node, autoTransmitOutgoing);
+    NODE_READ_UNSIGNED( this_node, windowFlags);
+    NODE_READ_NUM_T   ( this_node, pjmedia_vid_dev_index, defaultCaptureDevice);
+    NODE_READ_NUM_T   ( this_node, pjmedia_vid_dev_index, defaultRenderDevice);
+    NODE_READ_NUM_T   ( this_node, pjmedia_vid_stream_rc_method, rateControlMethod);
+    NODE_READ_UNSIGNED( this_node, rateControlBandwidth);
+}
+
+void AccountVideoConfig::writeObject(ContainerNode &node) const throw(Error)
+{
+    ContainerNode this_node = node.writeNewContainer("AccountVideoConfig");
+
+    NODE_WRITE_BOOL    ( this_node, autoShowIncoming);
+    NODE_WRITE_BOOL    ( this_node, autoTransmitOutgoing);
+    NODE_WRITE_UNSIGNED( this_node, windowFlags);
+    NODE_WRITE_NUM_T   ( this_node, pjmedia_vid_dev_index, defaultCaptureDevice);
+    NODE_WRITE_NUM_T   ( this_node, pjmedia_vid_dev_index, defaultRenderDevice);
+    NODE_WRITE_NUM_T   ( this_node, pjmedia_vid_stream_rc_method, rateControlMethod);
+    NODE_WRITE_UNSIGNED( this_node, rateControlBandwidth);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+AccountConfig::AccountConfig()
+{
+    pjsua_acc_config acc_cfg;
+    pjsua_acc_config_default(&acc_cfg);
+    pjsua_media_config med_cfg;
+    pjsua_media_config_default(&med_cfg);
+    fromPj(acc_cfg, &med_cfg);
+}
+
+/* Convert to pjsip. */
+void AccountConfig::toPj(pjsua_acc_config &ret) const
+{
+    unsigned i;
+
+    pjsua_acc_config_default(&ret);
+
+    // Global
+    ret.priority		= priority;
+    ret.id			= str2Pj(idUri);
+
+    // AccountRegConfig
+    ret.reg_uri			= str2Pj(regConfig.registrarUri);
+    ret.register_on_acc_add	= regConfig.registerOnAdd;
+    ret.reg_timeout		= regConfig.timeoutSec;
+    ret.reg_retry_interval	= regConfig.retryIntervalSec;
+    ret.reg_first_retry_interval= regConfig.firstRetryIntervalSec;
+    ret.reg_delay_before_refresh= regConfig.delayBeforeRefreshSec;
+    ret.drop_calls_on_reg_fail	= regConfig.dropCallsOnFail;
+    ret.unreg_timeout		= regConfig.unregWaitSec;
+    ret.reg_use_proxy		= regConfig.proxyUse;
+    for (i=0; i<regConfig.headers.size(); ++i) {
+	pj_list_push_back(&ret.reg_hdr_list, &regConfig.headers[i].toPj());
+    }
+
+    // AccountSipConfig
+    ret.cred_count = 0;
+    if (sipConfig.authCreds.size() > PJ_ARRAY_SIZE(ret.cred_info))
+	PJSUA2_RAISE_ERROR(PJ_ETOOMANY);
+    for (i=0; i<sipConfig.authCreds.size(); ++i) {
+	const AuthCredInfo &src = sipConfig.authCreds[i];
+	pjsip_cred_info *dst = &ret.cred_info[i];
+
+	dst->realm	= str2Pj(src.realm);
+	dst->scheme	= str2Pj(src.scheme);
+	dst->username	= str2Pj(src.username);
+	dst->data_type	= src.dataType;
+	dst->data	= str2Pj(src.data);
+	dst->ext.aka.k	= str2Pj(src.akaK);
+	dst->ext.aka.op	= str2Pj(src.akaOp);
+	dst->ext.aka.amf= str2Pj(src.akaAmf);
+
+	ret.cred_count++;
+    }
+    ret.proxy_cnt = 0;
+    if (sipConfig.proxies.size() > PJ_ARRAY_SIZE(ret.proxy))
+	PJSUA2_RAISE_ERROR(PJ_ETOOMANY);
+    for (i=0; i<sipConfig.proxies.size(); ++i) {
+	ret.proxy[ret.proxy_cnt++] = str2Pj(sipConfig.proxies[i]);
+    }
+    ret.force_contact		= str2Pj(sipConfig.contactForced);
+    ret.contact_params		= str2Pj(sipConfig.contactParams);
+    ret.contact_uri_params	= str2Pj(sipConfig.contactUriParams);
+    ret.auth_pref.initial_auth	= sipConfig.authInitialEmpty;
+    ret.auth_pref.algorithm	= str2Pj(sipConfig.authInitialAlgorithm);
+    ret.transport_id		= sipConfig.transportId;
+
+    // AccountCallConfig
+    ret.call_hold_type		= callConfig.holdType;
+    ret.require_100rel		= callConfig.prackUse;
+    ret.use_timer		= callConfig.timerUse;
+    ret.timer_setting.min_se	= callConfig.timerMinSESec;
+    ret.timer_setting.sess_expires = callConfig.timerSessExpiresSec;
+
+    // AccountPresConfig
+    for (i=0; i<presConfig.headers.size(); ++i) {
+	pj_list_push_back(&ret.sub_hdr_list, &presConfig.headers[i].toPj());
+    }
+    ret.publish_enabled		= presConfig.publishEnabled;
+    ret.publish_opt.queue_request= presConfig.publishQueue;
+    ret.unpublish_max_wait_time_msec = presConfig.publishShutdownWaitMsec;
+    ret.pidf_tuple_id		= str2Pj(presConfig.pidfTupleId);
+
+    // AccountNatConfig
+    ret.sip_stun_use		= natConfig.sipStunUse;
+    ret.media_stun_use		= natConfig.mediaStunUse;
+    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;
+    ret.ice_cfg.ice_opt.aggressive = natConfig.iceAggressiveNomination;
+    ret.ice_cfg.ice_opt.nominated_check_delay = natConfig.iceNominatedCheckDelayMsec;
+    ret.ice_cfg.ice_opt.controlled_agent_want_nom_timeout = natConfig.iceWaitNominationTimeoutMsec;
+    ret.ice_cfg.ice_no_rtcp	= natConfig.iceNoRtcp;
+    ret.ice_cfg.ice_always_update = natConfig.iceAlwaysUpdate;
+
+    ret.turn_cfg_use 		= PJSUA_TURN_CONFIG_USE_CUSTOM;
+    ret.turn_cfg.enable_turn	= natConfig.turnEnabled;
+    ret.turn_cfg.turn_server	= str2Pj(natConfig.turnServer);
+    ret.turn_cfg.turn_conn_type	= natConfig.turnConnType;
+    ret.turn_cfg.turn_auth_cred.type = PJ_STUN_AUTH_CRED_STATIC;
+    ret.turn_cfg.turn_auth_cred.data.static_cred.username = str2Pj(natConfig.turnUserName);
+    ret.turn_cfg.turn_auth_cred.data.static_cred.data_type = (pj_stun_passwd_type)natConfig.turnPasswordType;
+    ret.turn_cfg.turn_auth_cred.data.static_cred.data = str2Pj(natConfig.turnPassword);
+    ret.turn_cfg.turn_auth_cred.data.static_cred.realm = pj_str((char*)"");
+    ret.turn_cfg.turn_auth_cred.data.static_cred.nonce = pj_str((char*)"");
+
+    ret.allow_contact_rewrite	= natConfig.contactRewriteUse;
+    ret.contact_rewrite_method	= natConfig.contactRewriteMethod;
+    ret.allow_via_rewrite	= natConfig.viaRewriteUse;
+    ret.allow_sdp_nat_rewrite	= natConfig.sdpNatRewriteUse;
+    ret.use_rfc5626		= natConfig.sipOutboundUse;
+    ret.rfc5626_instance_id	= str2Pj(natConfig.sipOutboundInstanceId);
+    ret.rfc5626_reg_id		= str2Pj(natConfig.sipOutboundRegId);
+    ret.ka_interval		= natConfig.udpKaIntervalSec;
+    ret.ka_data			= str2Pj(natConfig.udpKaData);
+
+    // AccountMediaConfig
+    ret.rtp_cfg			= mediaConfig.transportConfig.toPj();
+    ret.lock_codec		= mediaConfig.lockCodecEnabled;
+#if defined(PJMEDIA_STREAM_ENABLE_KA) && (PJMEDIA_STREAM_ENABLE_KA != 0)
+    ret.use_stream_ka		= mediaConfig.streamKaEnabled;
+#endif
+    ret.use_srtp		= mediaConfig.srtpUse;
+    ret.srtp_secure_signaling	= mediaConfig.srtpSecureSignaling;
+    ret.ipv6_media_use		= mediaConfig.ipv6Use;
+
+    // AccountVideoConfig
+    ret.vid_in_auto_show	= videoConfig.autoShowIncoming;
+    ret.vid_out_auto_transmit	= videoConfig.autoTransmitOutgoing;
+    ret.vid_wnd_flags		= videoConfig.windowFlags;
+    ret.vid_cap_dev		= videoConfig.defaultCaptureDevice;
+    ret.vid_rend_dev		= videoConfig.defaultRenderDevice;
+    ret.vid_stream_rc_cfg.method= videoConfig.rateControlMethod;
+    ret.vid_stream_rc_cfg.bandwidth = videoConfig.rateControlBandwidth;
+}
+
+/* Initialize from pjsip. */
+void AccountConfig::fromPj(const pjsua_acc_config &prm,
+                           const pjsua_media_config *mcfg)
+{
+    const pjsip_hdr *hdr;
+    unsigned i;
+
+    // Global
+    priority			= prm.priority;
+    idUri			= pj2Str(prm.id);
+
+    // AccountRegConfig
+    regConfig.registrarUri	= pj2Str(prm.reg_uri);
+    regConfig.registerOnAdd	= (prm.register_on_acc_add != 0);
+    regConfig.timeoutSec	= prm.reg_timeout;
+    regConfig.retryIntervalSec	= prm.reg_retry_interval;
+    regConfig.firstRetryIntervalSec = prm.reg_first_retry_interval;
+    regConfig.delayBeforeRefreshSec = prm.reg_delay_before_refresh;
+    regConfig.dropCallsOnFail	= PJ2BOOL(prm.drop_calls_on_reg_fail);
+    regConfig.unregWaitSec	= prm.unreg_timeout;
+    regConfig.proxyUse		= prm.reg_use_proxy;
+    regConfig.headers.clear();
+    hdr = prm.reg_hdr_list.next;
+    while (hdr != &prm.reg_hdr_list) {
+	SipHeader new_hdr;
+	new_hdr.fromPj(hdr);
+
+	regConfig.headers.push_back(new_hdr);
+
+	hdr = hdr->next;
+    }
+
+    // AccountSipConfig
+    sipConfig.authCreds.clear();
+    for (i=0; i<prm.cred_count; ++i) {
+	AuthCredInfo cred;
+	const pjsip_cred_info &src = prm.cred_info[i];
+
+	cred.realm	= pj2Str(src.realm);
+	cred.scheme	= pj2Str(src.scheme);
+	cred.username	= pj2Str(src.username);
+	cred.dataType	= src.data_type;
+	cred.data	= pj2Str(src.data);
+	cred.akaK	= pj2Str(src.ext.aka.k);
+	cred.akaOp	= pj2Str(src.ext.aka.op);
+	cred.akaAmf	= pj2Str(src.ext.aka.amf);
+
+	sipConfig.authCreds.push_back(cred);
+    }
+    sipConfig.proxies.clear();
+    for (i=0; i<prm.proxy_cnt; ++i) {
+	sipConfig.proxies.push_back(pj2Str(prm.proxy[i]));
+    }
+    sipConfig.contactForced	= pj2Str(prm.force_contact);
+    sipConfig.contactParams	= pj2Str(prm.contact_params);
+    sipConfig.contactUriParams	= pj2Str(prm.contact_uri_params);
+    sipConfig.authInitialEmpty	= PJ2BOOL(prm.auth_pref.initial_auth);
+    sipConfig.authInitialAlgorithm = pj2Str(prm.auth_pref.algorithm);
+    sipConfig.transportId	= prm.transport_id;
+
+    // AccountCallConfig
+    callConfig.holdType		= prm.call_hold_type;
+    callConfig.prackUse		= prm.require_100rel;
+    callConfig.timerUse		= prm.use_timer;
+    callConfig.timerMinSESec	= prm.timer_setting.min_se;
+    callConfig.timerSessExpiresSec = prm.timer_setting.sess_expires;
+
+    // AccountPresConfig
+    presConfig.headers.clear();
+    hdr = prm.sub_hdr_list.next;
+    while (hdr != &prm.sub_hdr_list) {
+	SipHeader new_hdr;
+	new_hdr.fromPj(hdr);
+	presConfig.headers.push_back(new_hdr);
+	hdr = hdr->next;
+    }
+    presConfig.publishEnabled	= PJ2BOOL(prm.publish_enabled);
+    presConfig.publishQueue	= PJ2BOOL(prm.publish_opt.queue_request);
+    presConfig.publishShutdownWaitMsec = prm.unpublish_max_wait_time_msec;
+    presConfig.pidfTupleId	= pj2Str(prm.pidf_tuple_id);
+
+    // AccountMwiConfig
+    mwiConfig.enabled		= PJ2BOOL(prm.mwi_enabled);
+    mwiConfig.expirationSec	= prm.mwi_expires;
+
+    // AccountNatConfig
+    natConfig.sipStunUse	= prm.sip_stun_use;
+    natConfig.mediaStunUse	= prm.media_stun_use;
+    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;
+	natConfig.iceAggressiveNomination = PJ2BOOL(prm.ice_cfg.ice_opt.aggressive);
+	natConfig.iceNominatedCheckDelayMsec = prm.ice_cfg.ice_opt.nominated_check_delay;
+	natConfig.iceWaitNominationTimeoutMsec = prm.ice_cfg.ice_opt.controlled_agent_want_nom_timeout;
+	natConfig.iceNoRtcp	= PJ2BOOL(prm.ice_cfg.ice_no_rtcp);
+	natConfig.iceAlwaysUpdate = PJ2BOOL(prm.ice_cfg.ice_always_update);
+    } else {
+	pjsua_media_config default_mcfg;
+	if (!mcfg) {
+	    pjsua_media_config_default(&default_mcfg);
+	    mcfg = &default_mcfg;
+	}
+	natConfig.iceEnabled	= PJ2BOOL(mcfg->enable_ice);
+	natConfig.iceMaxHostCands= mcfg->ice_max_host_cands;
+	natConfig.iceAggressiveNomination = PJ2BOOL(mcfg->ice_opt.aggressive);
+	natConfig.iceNominatedCheckDelayMsec = mcfg->ice_opt.nominated_check_delay;
+	natConfig.iceWaitNominationTimeoutMsec = mcfg->ice_opt.controlled_agent_want_nom_timeout;
+	natConfig.iceNoRtcp	= PJ2BOOL(mcfg->ice_no_rtcp);
+	natConfig.iceAlwaysUpdate = PJ2BOOL(mcfg->ice_always_update);
+    }
+
+    if (prm.turn_cfg_use == PJSUA_TURN_CONFIG_USE_CUSTOM) {
+	natConfig.turnEnabled	= PJ2BOOL(prm.turn_cfg.enable_turn);
+	natConfig.turnServer	= pj2Str(prm.turn_cfg.turn_server);
+	natConfig.turnConnType	= prm.turn_cfg.turn_conn_type;
+	natConfig.turnUserName	= pj2Str(prm.turn_cfg.turn_auth_cred.data.static_cred.username);
+	natConfig.turnPasswordType = prm.turn_cfg.turn_auth_cred.data.static_cred.data_type;
+	natConfig.turnPassword	= pj2Str(prm.turn_cfg.turn_auth_cred.data.static_cred.data);
+    } else {
+	pjsua_media_config default_mcfg;
+	if (!mcfg) {
+	    pjsua_media_config_default(&default_mcfg);
+	    mcfg = &default_mcfg;
+	}
+	natConfig.turnEnabled	= PJ2BOOL(mcfg->enable_turn);
+	natConfig.turnServer	= pj2Str(mcfg->turn_server);
+	natConfig.turnConnType	= mcfg->turn_conn_type;
+	natConfig.turnUserName	= pj2Str(mcfg->turn_auth_cred.data.static_cred.username);
+	natConfig.turnPasswordType = mcfg->turn_auth_cred.data.static_cred.data_type;
+	natConfig.turnPassword	= pj2Str(mcfg->turn_auth_cred.data.static_cred.data);
+    }
+    natConfig.contactRewriteUse	= prm.allow_contact_rewrite;
+    natConfig.contactRewriteMethod = prm.contact_rewrite_method;
+    natConfig.viaRewriteUse	= prm.allow_via_rewrite;
+    natConfig.sdpNatRewriteUse	= prm.allow_sdp_nat_rewrite;
+    natConfig.sipOutboundUse	= prm.use_rfc5626;
+    natConfig.sipOutboundInstanceId = pj2Str(prm.rfc5626_instance_id);
+    natConfig.sipOutboundRegId	= pj2Str(prm.rfc5626_reg_id);
+    natConfig.udpKaIntervalSec	= prm.ka_interval;
+    natConfig.udpKaData		= pj2Str(prm.ka_data);
+
+    // AccountMediaConfig
+    mediaConfig.transportConfig.fromPj(prm.rtp_cfg);
+    mediaConfig.lockCodecEnabled= PJ2BOOL(prm.lock_codec);
+#if defined(PJMEDIA_STREAM_ENABLE_KA) && (PJMEDIA_STREAM_ENABLE_KA != 0)
+    mediaConfig.streamKaEnabled	= PJ2BOOL(prm.use_stream_ka);
+#else
+    mediaConfig.streamKaEnabled	= false;
+#endif
+    mediaConfig.srtpUse		= prm.use_srtp;
+    mediaConfig.srtpSecureSignaling = prm.srtp_secure_signaling;
+    mediaConfig.ipv6Use		= prm.ipv6_media_use;
+
+    // AccountVideoConfig
+    videoConfig.autoShowIncoming 	= PJ2BOOL(prm.vid_in_auto_show);
+    videoConfig.autoTransmitOutgoing	= PJ2BOOL(prm.vid_out_auto_transmit);
+    videoConfig.windowFlags		= prm.vid_wnd_flags;
+    videoConfig.defaultCaptureDevice	= prm.vid_cap_dev;
+    videoConfig.defaultRenderDevice	= prm.vid_rend_dev;
+    videoConfig.rateControlMethod	= prm.vid_stream_rc_cfg.method;
+    videoConfig.rateControlBandwidth	= prm.vid_stream_rc_cfg.bandwidth;
+}
+
+void AccountConfig::readObject(const ContainerNode &node) throw(Error)
+{
+    ContainerNode this_node = node.readContainer("AccountConfig");
+
+    NODE_READ_INT     ( this_node, priority);
+    NODE_READ_STRING  ( this_node, idUri);
+    NODE_READ_OBJ     ( this_node, regConfig);
+    NODE_READ_OBJ     ( this_node, sipConfig);
+    NODE_READ_OBJ     ( this_node, callConfig);
+    NODE_READ_OBJ     ( this_node, presConfig);
+    NODE_READ_OBJ     ( this_node, mwiConfig);
+    NODE_READ_OBJ     ( this_node, natConfig);
+    NODE_READ_OBJ     ( this_node, mediaConfig);
+    NODE_READ_OBJ     ( this_node, videoConfig);
+}
+
+void AccountConfig::writeObject(ContainerNode &node) const throw(Error)
+{
+    ContainerNode this_node = node.writeNewContainer("AccountConfig");
+
+    NODE_WRITE_INT     ( this_node, priority);
+    NODE_WRITE_STRING  ( this_node, idUri);
+    NODE_WRITE_OBJ     ( this_node, regConfig);
+    NODE_WRITE_OBJ     ( this_node, sipConfig);
+    NODE_WRITE_OBJ     ( this_node, callConfig);
+    NODE_WRITE_OBJ     ( this_node, presConfig);
+    NODE_WRITE_OBJ     ( this_node, mwiConfig);
+    NODE_WRITE_OBJ     ( this_node, natConfig);
+    NODE_WRITE_OBJ     ( this_node, mediaConfig);
+    NODE_WRITE_OBJ     ( this_node, videoConfig);
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+void AccountInfo::fromPj(const pjsua_acc_info &pai)
+{
+    id 			= pai.id;
+    isDefault 		= pai.is_default != 0;
+    uri			= pj2Str(pai.acc_uri);
+    regIsConfigured	= pai.has_registration != 0;
+    regIsActive		= pai.has_registration && pai.expires > 0 &&
+			    (pai.status / 100 == 2);
+    regExpiresSec	= pai.expires;
+    regStatus		= pai.status;
+    regStatusText	= pj2Str(pai.status_text);
+    regLastErr		= pai.reg_last_err;
+    onlineStatus	= pai.online_status != 0;
+    onlineStatusText	= pj2Str(pai.online_status_text);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+Account::Account()
+: id(PJSUA_INVALID_ID)
+{
+}
+
+Account::~Account()
+{
+    /* If this instance is deleted, also delete the corresponding account in
+     * PJSUA library.
+     */
+    if (isValid() && pjsua_get_state() < PJSUA_STATE_CLOSING) {
+        // Cleanup buddies in the buddy list
+	while(buddyList.size() > 0) {
+	    Buddy *b = buddyList[0];
+	    delete b; /* this will remove itself from the list */
+	}
+
+	pjsua_acc_set_user_data(id, NULL);
+	pjsua_acc_del(id);
+    }
+}
+
+void Account::create(const AccountConfig &acc_cfg,
+                     bool make_default) throw(Error)
+{
+    pjsua_acc_config pj_acc_cfg;
+    
+    acc_cfg.toPj(pj_acc_cfg);
+    pj_acc_cfg.user_data = (void*)this;
+    PJSUA2_CHECK_EXPR( pjsua_acc_add(&pj_acc_cfg, make_default, &id) );
+}
+
+void Account::modify(const AccountConfig &acc_cfg) throw(Error)
+{
+    pjsua_acc_config pj_acc_cfg;
+    
+    acc_cfg.toPj(pj_acc_cfg);
+    pj_acc_cfg.user_data = (void*)this;
+    PJSUA2_CHECK_EXPR( pjsua_acc_modify(id, &pj_acc_cfg) );
+}
+
+bool Account::isValid() const
+{
+    return pjsua_acc_is_valid(id) != 0;
+}
+
+void Account::setDefault() throw(Error)
+{
+    PJSUA2_CHECK_EXPR( pjsua_acc_set_default(id) );
+}
+
+bool Account::isDefault() const
+{
+    return pjsua_acc_get_default() == id;
+}
+
+int Account::getId() const
+{
+    return id;
+}
+
+Account *Account::lookup(int acc_id)
+{
+    return (Account*)pjsua_acc_get_user_data(acc_id);
+}
+
+AccountInfo Account::getInfo() const throw(Error)
+{
+    pjsua_acc_info pj_ai;
+    AccountInfo ai;
+
+    PJSUA2_CHECK_EXPR( pjsua_acc_get_info(id, &pj_ai) );
+    ai.fromPj(pj_ai);
+    return ai;
+}
+
+void Account::setRegistration(bool renew) throw(Error)
+{
+    PJSUA2_CHECK_EXPR( pjsua_acc_set_registration(id, renew) );
+}
+
+void
+Account::setOnlineStatus(const PresenceStatus &pres_st) throw(Error)
+{
+    pjrpid_element pj_rpid;
+
+    pj_bzero(&pj_rpid, sizeof(pj_rpid));
+    pj_rpid.type	= PJRPID_ELEMENT_TYPE_PERSON;
+    pj_rpid.activity	= pres_st.activity;
+    pj_rpid.id		= str2Pj(pres_st.rpidId);
+    pj_rpid.note	= str2Pj(pres_st.note);
+
+    PJSUA2_CHECK_EXPR( pjsua_acc_set_online_status2(
+			    id, pres_st.status == PJSUA_BUDDY_STATUS_ONLINE,
+			    &pj_rpid) );
+}
+
+void Account::setTransport(TransportId tp_id) throw(Error)
+{
+    PJSUA2_CHECK_EXPR( pjsua_acc_set_transport(id, tp_id) );
+}
+
+void Account::presNotify(const PresNotifyParam &prm) throw(Error)
+{
+    pj_str_t pj_state_str   = str2Pj(prm.stateStr);
+    pj_str_t pj_reason	    = str2Pj(prm.reason);
+    pjsua_msg_data msg_data;
+    prm.txOption.toPj(msg_data);
+
+    PJSUA2_CHECK_EXPR( pjsua_pres_notify(id, (pjsua_srv_pres*)prm.srvPres,
+					 prm.state, &pj_state_str,
+					 &pj_reason, prm.withBody,
+					 &msg_data) );
+}
+
+const BuddyVector& Account::enumBuddies() const throw(Error)
+{
+    return buddyList;
+}
+
+Buddy* Account::findBuddy(string uri, FindBuddyMatch *buddy_match) const
+		throw(Error)
+{
+    if (!buddy_match) {
+	static FindBuddyMatch def_bm;
+	buddy_match = &def_bm;
+    }
+
+    for (unsigned i = 0; i < buddyList.size(); i++) {
+	if (buddy_match->match(uri, *buddyList[i]))
+	    return buddyList[i];
+    }
+    PJSUA2_RAISE_ERROR(PJ_ENOTFOUND);
+}
+
+void Account::addBuddy(Buddy *buddy)
+{
+    pj_assert(buddy);
+
+    buddyList.push_back(buddy);
+}
+
+void Account::removeBuddy(Buddy *buddy)
+{
+    pj_assert(buddy);
+
+    BuddyVector::iterator it;
+    for (it = buddyList.begin(); it != buddyList.end(); it++) {
+	if (*it == buddy) {
+	    buddyList.erase(it);
+	    return;
+	}
+    }
+
+    pj_assert(!"Bug! Buddy to be removed is not in the buddy list!");
+}
Index: /pjproject/trunk/pjsip/src/pjsua2/call.cpp
===================================================================
--- /pjproject/trunk/pjsip/src/pjsua2/call.cpp	(revision 4704)
+++ /pjproject/trunk/pjsip/src/pjsua2/call.cpp	(revision 4704)
@@ -0,0 +1,719 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2012-2013 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 <pjsua2/account.hpp>
+#include <pjsua2/call.hpp>
+#include <pjsua2/endpoint.hpp>
+#include <pj/ctype.h>
+#include "util.hpp"
+
+using namespace pj;
+using namespace std;
+
+#define THIS_FILE		"call.cpp"
+
+///////////////////////////////////////////////////////////////////////////////
+
+#define SDP_BUFFER_SIZE 1024
+
+MathStat::MathStat()
+: n(0), max(0), min(0), last(0), mean(0)
+{
+}
+
+void MathStat::fromPj(const pj_math_stat &prm)
+{
+    this->n    = prm.n;
+    this->max  = prm.max;
+    this->min  = prm.min;
+    this->last = prm.last;
+    this->mean = prm.mean;
+}
+
+void RtcpStreamStat::fromPj(const pjmedia_rtcp_stream_stat &prm)
+{
+    this->update.fromPj(prm.update);
+    this->updateCount     = prm.update_cnt;
+    this->pkt             = (unsigned)prm.pkt;
+    this->bytes           = (unsigned)prm.bytes;
+    this->discard         = prm.discard;
+    this->loss            = prm.loss;
+    this->reorder         = prm.loss;
+    this->dup             = prm.dup;
+    this->lossPeriodUsec.fromPj(prm.loss_period);
+    this->lossType.burst  = prm.loss_type.burst;
+    this->lossType.random = prm.loss_type.random;
+    this->jitterUsec.fromPj(prm.jitter);
+}
+
+void RtcpSdes::fromPj(const pjmedia_rtcp_sdes &prm)
+{
+    this->cname = pj2Str(prm.cname);
+    this->name  = pj2Str(prm.name);
+    this->email = pj2Str(prm.email);
+    this->phone = pj2Str(prm.phone);
+    this->loc   = pj2Str(prm.loc);
+    this->tool  = pj2Str(prm.tool);
+    this->note  = pj2Str(prm.note);
+}
+
+void RtcpStat::fromPj(const pjmedia_rtcp_stat &prm)
+{
+    this->start.fromPj(prm.start);
+    this->txStat.fromPj(prm.tx);
+    this->rxStat.fromPj(prm.rx);
+    this->rttUsec.fromPj(prm.rtt);
+    this->rtpTxLastTs  = prm.rtp_tx_last_ts;
+    this->rtpTxLastSeq = prm.rtp_tx_last_seq;
+#if defined(PJMEDIA_RTCP_STAT_HAS_IPDV) && PJMEDIA_RTCP_STAT_HAS_IPDV!=0
+    this->rxIpdvUsec.fromPj(prm.rx_ipdv);
+#endif
+#if defined(PJMEDIA_RTCP_STAT_HAS_RAW_JITTER) && \
+    PJMEDIA_RTCP_STAT_HAS_RAW_JITTER!=0
+    this->rxRawJitterUsec.fromPj(prm.rx_raw_jitter);
+#endif
+    this->peerSdes.fromPj(prm.peer_sdes);
+}
+
+void JbufState::fromPj(const pjmedia_jb_state &prm)
+{
+    this->frameSize    = prm.frame_size;
+    this->minPrefetch  = prm.min_prefetch;
+    this->maxPrefetch  = prm.max_prefetch;
+    this->burst        = prm.burst;
+    this->prefetch     = prm.prefetch;
+    this->size         = prm.size;
+    this->avgDelayMsec = prm.avg_delay;
+    this->minDelayMsec = prm.min_delay;
+    this->maxDelayMsec = prm.max_delay;
+    this->devDelayMsec = prm.dev_delay;
+    this->avgBurst     = prm.avg_burst;
+    this->lost         = prm.lost;
+    this->discard      = prm.discard;
+    this->empty        = prm.empty;
+}
+
+void SdpSession::fromPj(const pjmedia_sdp_session &sdp)
+{
+    char buf[SDP_BUFFER_SIZE];
+    int len;
+
+    len = pjmedia_sdp_print(&sdp, buf, sizeof(buf));
+    wholeSdp = (len > -1? string(buf, len): "");
+    pjSdpSession = (void *)&sdp;
+}
+
+void MediaEvent::fromPj(const pjmedia_event &ev)
+{
+    type = ev.type;
+    if (type == PJMEDIA_EVENT_FMT_CHANGED) {
+        data.fmtChanged.newWidth = ev.data.fmt_changed.new_fmt.det.vid.size.w;
+        data.fmtChanged.newHeight = ev.data.fmt_changed.new_fmt.det.vid.size.h;
+    }
+    pjMediaEvent = (void *)&ev;
+}
+
+void MediaTransportInfo::fromPj(const pjmedia_transport_info &info)
+{
+    char straddr[PJ_INET6_ADDRSTRLEN+10];
+    
+    pj_sockaddr_print(&info.src_rtp_name, straddr, sizeof(straddr), 3);
+    srcRtpName = straddr;
+    pj_sockaddr_print(&info.src_rtcp_name, straddr, sizeof(straddr), 3);
+    srcRtcpName = straddr;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+/* Call Audio Media. */
+class CallAudioMedia : public AudioMedia
+{
+public:
+    /*
+     * Set the conference port identification associated with the
+     * call audio media.
+     */
+    void setPortId(int id);
+};
+
+
+void CallAudioMedia::setPortId(int id)
+{
+    this->id = id;
+}
+
+CallOpParam::CallOpParam(bool useDefaultCallSetting)
+: statusCode(pjsip_status_code(0)), reason(""), options(0)
+{
+    if (useDefaultCallSetting)
+        opt = CallSetting(true);
+}
+
+CallSendRequestParam::CallSendRequestParam()
+: method("")
+{
+}
+
+CallVidSetStreamParam::CallVidSetStreamParam()
+{
+#if PJSUA_HAS_VIDEO
+    pjsua_call_vid_strm_op_param prm;
+    
+    pjsua_call_vid_strm_op_param_default(&prm);
+    this->medIdx = prm.med_idx;
+    this->dir    = prm.dir;
+    this->capDev = prm.cap_dev;
+#endif
+}
+
+CallSetting::CallSetting(pj_bool_t useDefaultValues)
+{
+    if (useDefaultValues) {
+        pjsua_call_setting setting;
+    
+        pjsua_call_setting_default(&setting);
+        fromPj(setting);
+    } else {
+        flag                = 0;
+        reqKeyframeMethod   = 0;
+        audioCount          = 0;
+        videoCount          = 0;
+    }
+}
+
+bool CallSetting::isEmpty() const
+{
+    return (flag == 0 && reqKeyframeMethod == 0 && audioCount == 0 &&
+            videoCount == 0);
+}
+
+void CallSetting::fromPj(const pjsua_call_setting &prm)
+{
+    this->flag              = prm.flag;
+    this->reqKeyframeMethod = prm.req_keyframe_method;
+    this->audioCount        = prm.aud_cnt;
+    this->videoCount        = prm.vid_cnt;
+}
+
+pjsua_call_setting CallSetting::toPj() const
+{
+    pjsua_call_setting setting;
+
+    setting.flag                = this->flag;
+    setting.req_keyframe_method = this->reqKeyframeMethod;
+    setting.aud_cnt             = this->audioCount;
+    setting.vid_cnt             = this->videoCount;
+    
+    return setting;
+}
+
+
+CallMediaInfo::CallMediaInfo()
+{
+}
+
+void CallMediaInfo::fromPj(const pjsua_call_media_info &prm)
+{
+    this->index                     = prm.index;
+    this->type                      = prm.type;
+    this->dir                       = prm.dir;
+    this->status                    = prm.status;
+    if (this->type == PJMEDIA_TYPE_AUDIO) {
+        this->audioConfSlot         = (int)prm.stream.aud.conf_slot;
+    } else if (this->type == PJMEDIA_TYPE_VIDEO) {
+        this->videoIncomingWindowId = prm.stream.vid.win_in;
+        this->videoCapDev           = prm.stream.vid.cap_dev;
+    }
+}
+
+void CallInfo::fromPj(const pjsua_call_info &pci)
+{
+    unsigned mi;
+    
+    id 			= pci.id;
+    role                = pci.role;
+    accId               = pci.acc_id;
+    localUri            = pj2Str(pci.local_info);
+    localContact        = pj2Str(pci.local_contact);
+    remoteUri           = pj2Str(pci.remote_info);
+    remoteContact       = pj2Str(pci.remote_contact);
+    callIdString        = pj2Str(pci.call_id);
+    setting.fromPj(pci.setting);
+    state               = pci.state;
+    stateText           = pj2Str(pci.state_text);
+    lastStatusCode      = pci.last_status;
+    lastReason          = pj2Str(pci.last_status_text);
+    connectDuration.fromPj(pci.connect_duration);
+    totalDuration.fromPj(pci.total_duration);
+    remOfferer          = PJ2BOOL(pci.rem_offerer);
+    remAudioCount       = pci.rem_aud_cnt;
+    remVideoCount       = pci.rem_vid_cnt;
+    
+    for (mi = 0; mi < pci.media_cnt; mi++) {
+        CallMediaInfo med;
+        
+        med.fromPj(pci.media[mi]);
+        media.push_back(med);
+    }
+    for (mi = 0; mi < pci.prov_media_cnt; mi++) {
+        CallMediaInfo med;
+        
+        med.fromPj(pci.prov_media[mi]);
+        provMedia.push_back(med);
+    }
+}
+
+void StreamInfo::fromPj(const pjsua_stream_info &info)
+{
+    char straddr[PJ_INET6_ADDRSTRLEN+10];
+
+    type = info.type;
+    if (type == PJMEDIA_TYPE_AUDIO) {
+        proto = info.info.aud.proto;
+        dir = info.info.aud.dir;
+        pj_sockaddr_print(&info.info.aud.rem_addr, straddr, sizeof(straddr), 3);
+        remoteRtpAddress = straddr;
+        pj_sockaddr_print(&info.info.aud.rem_rtcp, straddr, sizeof(straddr), 3);
+        remoteRtcpAddress = straddr;
+        txPt = info.info.aud.tx_pt;
+        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;
+    } else if (type == PJMEDIA_TYPE_VIDEO) {
+        proto = info.info.vid.proto;
+        dir = info.info.vid.dir;
+        pj_sockaddr_print(&info.info.vid.rem_addr, straddr, sizeof(straddr), 3);
+        remoteRtpAddress = straddr;
+        pj_sockaddr_print(&info.info.vid.rem_rtcp, straddr, sizeof(straddr), 3);
+        remoteRtcpAddress = straddr;
+        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;
+    }
+}
+
+void StreamStat::fromPj(const pjsua_stream_stat &prm)
+{
+    rtcp.fromPj(prm.rtcp);
+    jbuf.fromPj(prm.jbuf);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+struct call_param
+{
+    pjsua_msg_data      msg_data;
+    pjsua_msg_data     *p_msg_data;
+    pjsua_call_setting  opt;
+    pjsua_call_setting *p_opt;
+    pj_str_t            reason;
+    pj_str_t           *p_reason;
+
+public:
+    /**
+     * Default constructors with specified parameters.
+     */
+    call_param(const SipTxOption &tx_option);
+    call_param(const SipTxOption &tx_option, const CallSetting &setting,
+               const string &reason_str);
+};
+
+call_param::call_param(const SipTxOption &tx_option)
+{
+    if (tx_option.isEmpty()) {
+        p_msg_data = NULL;
+    } else {
+        tx_option.toPj(msg_data);
+        p_msg_data = &msg_data;
+    }
+    
+    p_opt = NULL;
+    p_reason = NULL;
+}
+
+call_param::call_param(const SipTxOption &tx_option, const CallSetting &setting,
+                       const string &reason_str)
+{
+    if (tx_option.isEmpty()) {
+        p_msg_data = NULL;
+    } else {
+        tx_option.toPj(msg_data);
+        p_msg_data = &msg_data;
+    }
+    
+    if (setting.isEmpty()) {
+        p_opt = NULL;
+    } else {
+        opt = setting.toPj();
+        p_opt = &opt;
+    }
+    
+    reason = str2Pj(reason_str);
+    p_reason = (reason.slen == 0? NULL: &reason);
+}
+
+Call::Call(Account& account, int call_id)
+: acc(account), id(call_id)
+{
+    if (call_id != PJSUA_INVALID_ID)
+        pjsua_call_set_user_data(call_id, this);
+}
+
+Call::~Call()
+{
+    /**
+     * If this instance is deleted, also hangup the corresponding call in
+     * PJSUA library.
+     */
+    if (id != PJSUA_INVALID_ID && pjsua_get_state() < PJSUA_STATE_CLOSING) {
+	pjsua_call_set_user_data(id, NULL);
+        if (isActive()) {
+            CallOpParam prm;
+            hangup(prm);
+        }
+    }
+}
+
+CallInfo Call::getInfo() const throw(Error)
+{
+    pjsua_call_info pj_ci;
+    CallInfo ci;
+    
+    PJSUA2_CHECK_EXPR( pjsua_call_get_info(id, &pj_ci) );
+    ci.fromPj(pj_ci);
+    return ci;
+}
+
+bool Call::isActive() const
+{
+    if (id == PJSUA_INVALID_ID)
+        return false;
+    
+    return (pjsua_call_is_active(id) != 0);
+}
+
+int Call::getId() const
+{
+    return id;
+}
+
+Call *Call::lookup(int call_id)
+{
+    Call *call = (Call*)pjsua_call_get_user_data(call_id);
+    if (call)
+        call->id = call_id;
+    return call;
+}
+
+bool Call::hasMedia() const
+{
+    return (pjsua_call_has_media(id) != 0);
+}
+
+Media *Call::getMedia(unsigned med_idx) const
+{
+    /* Check if the media index is valid and if the media has a valid port ID */
+    if (med_idx >= medias.size() ||
+        (medias[med_idx] && medias[med_idx]->getType() == PJMEDIA_TYPE_AUDIO &&
+         ((AudioMedia *)medias[med_idx])->getPortId() == PJSUA_INVALID_ID))
+    {
+        return NULL;
+    }
+    
+    return medias[med_idx];
+}
+
+pjsip_dialog_cap_status Call::remoteHasCap(int htype,
+                                           const string &hname,
+                                           const string &token) const
+{
+    pj_str_t pj_hname = str2Pj(hname);
+    pj_str_t pj_token = str2Pj(token);
+    
+    return pjsua_call_remote_has_cap(id, htype,
+                                     (htype == PJSIP_H_OTHER)? &pj_hname: NULL,
+                                     &pj_token);
+}
+
+void Call::setUserData(Token user_data)
+{
+    userData = user_data;
+}
+
+Token Call::getUserData() const
+{
+    return userData;
+}
+
+pj_stun_nat_type Call::getRemNatType() throw(Error)
+{
+    pj_stun_nat_type nat;
+    
+    PJSUA2_CHECK_EXPR( pjsua_call_get_rem_nat_type(id, &nat) );
+    
+    return nat;
+}
+
+void Call::makeCall(const string &dst_uri, const CallOpParam &prm) throw(Error)
+{
+    pj_str_t pj_dst_uri = str2Pj(dst_uri);
+    call_param param(prm.txOption, prm.opt, prm.reason);
+    
+    PJSUA2_CHECK_EXPR( pjsua_call_make_call(acc.getId(), &pj_dst_uri,
+                                            param.p_opt, this,
+                                            param.p_msg_data, &id) );
+}
+
+void Call::answer(const CallOpParam &prm) throw(Error)
+{
+    call_param param(prm.txOption, prm.opt, prm.reason);
+    
+    PJSUA2_CHECK_EXPR( pjsua_call_answer2(id, param.p_opt, prm.statusCode,
+                                          param.p_reason, param.p_msg_data) );
+}
+
+void Call::hangup(const CallOpParam &prm) throw(Error)
+{
+    call_param param(prm.txOption, prm.opt, prm.reason);
+    
+    PJSUA2_CHECK_EXPR( pjsua_call_hangup(id, prm.statusCode, param.p_reason,
+                                         param.p_msg_data) );
+}
+
+void Call::setHold(const CallOpParam &prm) throw(Error)
+{
+    call_param param(prm.txOption, prm.opt, prm.reason);
+    
+    PJSUA2_CHECK_EXPR( pjsua_call_set_hold2(id, prm.options, param.p_msg_data));
+}
+
+void Call::reinvite(const CallOpParam &prm) throw(Error)
+{
+    call_param param(prm.txOption, prm.opt, prm.reason);
+
+    PJSUA2_CHECK_EXPR( pjsua_call_reinvite2(id, param.p_opt, param.p_msg_data));
+}
+
+void Call::update(const CallOpParam &prm) throw(Error)
+{
+    call_param param(prm.txOption, prm.opt, prm.reason);
+    
+    PJSUA2_CHECK_EXPR( pjsua_call_update2(id, param.p_opt, param.p_msg_data) );
+}
+
+void Call::xfer(const string &dest, const CallOpParam &prm) throw(Error)
+{
+    call_param param(prm.txOption);
+    pj_str_t pj_dest = str2Pj(dest);
+    
+    PJSUA2_CHECK_EXPR( pjsua_call_xfer(id, &pj_dest, param.p_msg_data) );
+}
+
+void Call::xferReplaces(const Call& dest_call,
+                  const CallOpParam &prm) throw(Error)
+{
+    call_param param(prm.txOption);
+    
+    PJSUA2_CHECK_EXPR(pjsua_call_xfer_replaces(id, dest_call.getId(),
+                                               prm.options, param.p_msg_data) );
+}
+
+void Call::processRedirect(pjsip_redirect_op cmd) throw(Error)
+{
+    PJSUA2_CHECK_EXPR(pjsua_call_process_redirect(id, cmd));
+}
+
+void Call::dialDtmf(const string &digits) throw(Error)
+{
+    pj_str_t pj_digits = str2Pj(digits);
+    
+    PJSUA2_CHECK_EXPR(pjsua_call_dial_dtmf(id, &pj_digits));
+}
+
+void Call::sendInstantMessage(const SendInstantMessageParam& prm)
+    throw(Error)
+{
+    pj_str_t mime_type = str2Pj(prm.contentType);
+    pj_str_t content = str2Pj(prm.content);
+    call_param param(prm.txOption);
+
+    PJSUA2_CHECK_EXPR(pjsua_call_send_im(id, &mime_type, &content,
+                                         param.p_msg_data, prm.userData) );
+}
+
+void Call::sendTypingIndication(const SendTypingIndicationParam &prm)
+    throw(Error)
+{
+    call_param param(prm.txOption);
+    
+    PJSUA2_CHECK_EXPR(pjsua_call_send_typing_ind(id,
+                                                 (prm.isTyping?
+                                                  PJ_TRUE: PJ_FALSE),
+                                                 param.p_msg_data) );
+}
+
+void Call::sendRequest(const CallSendRequestParam &prm) throw(Error)
+{
+    pj_str_t method = str2Pj(prm.method);
+    call_param param(prm.txOption);
+    
+    PJSUA2_CHECK_EXPR(pjsua_call_send_request(id, &method, param.p_msg_data) );
+}
+
+string Call::dump(bool with_media, const string indent) throw(Error)
+{
+#if defined(PJMEDIA_HAS_RTCP_XR) && (PJMEDIA_HAS_RTCP_XR != 0)
+    char buffer[1024 * 10];
+#else
+    char buffer[1024 * 3];
+#endif
+
+    PJSUA2_CHECK_EXPR(pjsua_call_dump(id, (with_media? PJ_TRUE: PJ_FALSE),
+                                      buffer, sizeof(buffer),
+                                      indent.c_str()));
+    
+    return buffer;
+}
+
+int Call::vidGetStreamIdx() const
+{
+#if PJSUA_HAS_VIDEO
+    return pjsua_call_get_vid_stream_idx(id);
+#else
+    return PJSUA_INVALID_ID;
+#endif
+}
+
+bool Call::vidStreamIsRunning(int med_idx, pjmedia_dir dir) const
+{
+#if PJSUA_HAS_VIDEO
+    return pjsua_call_vid_stream_is_running(id, med_idx, dir);
+#else
+    PJ_UNUSED_ARG(med_idx);
+    PJ_UNUSED_ARG(dir);
+    return false;
+#endif
+}
+
+void Call::vidSetStream(pjsua_call_vid_strm_op op,
+                        const CallVidSetStreamParam &param) throw(Error)
+{
+#if PJSUA_HAS_VIDEO
+    pjsua_call_vid_strm_op_param prm;
+    
+    prm.med_idx = param.medIdx;
+    prm.dir = param.dir;
+    prm.cap_dev = param.capDev;
+    PJSUA2_CHECK_EXPR( pjsua_call_set_vid_strm(id, op, &prm) );
+#else
+    PJ_UNUSED_ARG(op);
+    PJ_UNUSED_ARG(param);
+    PJSUA2_RAISE_ERROR(PJ_EINVALIDOP);
+#endif
+}
+
+StreamInfo Call::getStreamInfo(unsigned med_idx) const throw(Error)
+{
+    pjsua_stream_info pj_si;
+    StreamInfo si;
+    
+    PJSUA2_CHECK_EXPR( pjsua_call_get_stream_info(id, med_idx, &pj_si) );
+    si.fromPj(pj_si);
+    return si;
+}
+
+StreamStat Call::getStreamStat(unsigned med_idx) const throw(Error)
+{
+    pjsua_stream_stat pj_ss;
+    StreamStat ss;
+    
+    PJSUA2_CHECK_EXPR( pjsua_call_get_stream_stat(id, med_idx, &pj_ss) );
+    ss.fromPj(pj_ss);
+    return ss;
+}
+
+MediaTransportInfo Call::getMedTransportInfo(unsigned med_idx) const
+    throw(Error)
+{
+    pjmedia_transport_info pj_mti;
+    MediaTransportInfo mti;
+    
+    PJSUA2_CHECK_EXPR( pjsua_call_get_med_transport_info(id, med_idx,
+                                                         &pj_mti) );
+    mti.fromPj(pj_mti);
+    return mti;
+}
+
+void Call::processMediaUpdate(OnCallMediaStateParam &prm)
+{
+    pjsua_call_info pj_ci;
+    unsigned mi;
+    
+    if (pjsua_call_get_info(id, &pj_ci) == PJ_SUCCESS) {
+        for (mi = 0; mi < pj_ci.media_cnt; mi++) {
+            if (mi >= medias.size()) {
+                if (pj_ci.media[mi].type == PJMEDIA_TYPE_AUDIO) {
+                    medias.push_back(new CallAudioMedia);
+                } else {
+                    medias.push_back(NULL);
+                }
+            }
+            
+            if (pj_ci.media[mi].type == PJMEDIA_TYPE_AUDIO) {
+                CallAudioMedia *aud_med = (CallAudioMedia *)medias[mi];
+                
+                aud_med->setPortId(pj_ci.media[mi].stream.aud.conf_slot);
+                /* Add media if the conference slot ID is valid. */
+                if (pj_ci.media[mi].stream.aud.conf_slot != PJSUA_INVALID_ID)
+                {
+                    Endpoint::instance().mediaAdd((AudioMedia &)*aud_med);
+                } else {
+                    Endpoint::instance().mediaRemove((AudioMedia &)*aud_med);
+                }
+            }
+        }
+    }
+    
+    /* Call media state callback. */
+    onCallMediaState(prm);
+}
+
+void Call::processStateChange(OnCallStateParam &prm)
+{
+    pjsua_call_info pj_ci;
+    unsigned mi;
+    
+    if (pjsua_call_get_info(id, &pj_ci) == PJ_SUCCESS &&
+        pj_ci.state == PJSIP_INV_STATE_DISCONNECTED)
+    {
+        /* Clear medias. */
+        for (mi = 0; mi < medias.size(); mi++) {
+            if (medias[mi])
+                delete medias[mi];
+        }
+        medias.clear();
+    }
+    
+    onCallState(prm);
+    /* If the state is DISCONNECTED, this call may have already been deleted
+     * by the application in the callback, so do not access it anymore here.
+     */
+}
Index: /pjproject/trunk/pjsip/src/pjsua2/endpoint.cpp
===================================================================
--- /pjproject/trunk/pjsip/src/pjsua2/endpoint.cpp	(revision 4704)
+++ /pjproject/trunk/pjsip/src/pjsua2/endpoint.cpp	(revision 4704)
@@ -0,0 +1,1612 @@
+/* $Id$ */
+/* 
+ * Copyright (C) 2013 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 <pjsua2/endpoint.hpp>
+#include <pjsua2/account.hpp>
+#include <pjsua2/call.hpp>
+#include <pjsua2/presence.hpp>
+#include <algorithm>
+#include "util.hpp"
+
+using namespace pj;
+using namespace std;
+
+#include <pjsua2/account.hpp>
+#include <pjsua2/call.hpp>
+
+#define THIS_FILE		"endpoint.cpp"
+#define MAX_STUN_SERVERS	32
+#define TIMER_SIGNATURE		0x600D878A
+#define MAX_CODEC_NUM 		64
+
+struct UserTimer
+{
+    pj_uint32_t		signature;
+    OnTimerParam	prm;
+    pj_timer_entry	entry;
+};
+
+Endpoint *Endpoint::instance_;
+
+///////////////////////////////////////////////////////////////////////////////
+
+UaConfig::UaConfig()
+{
+    pjsua_config ua_cfg;
+
+    pjsua_config_default(&ua_cfg);
+    fromPj(ua_cfg);
+}
+
+void UaConfig::fromPj(const pjsua_config &ua_cfg)
+{
+    unsigned i;
+
+    this->maxCalls = ua_cfg.max_calls;
+    this->threadCnt = ua_cfg.thread_cnt;
+    this->userAgent = pj2Str(ua_cfg.user_agent);
+
+    for (i=0; i<ua_cfg.nameserver_count; ++i) {
+	this->nameserver.push_back(pj2Str(ua_cfg.nameserver[i]));
+    }
+
+    for (i=0; i<ua_cfg.stun_srv_cnt; ++i) {
+	this->stunServer.push_back(pj2Str(ua_cfg.stun_srv[i]));
+    }
+
+    this->stunIgnoreFailure = PJ2BOOL(ua_cfg.stun_ignore_failure);
+    this->natTypeInSdp = ua_cfg.nat_type_in_sdp;
+    this->mwiUnsolicitedEnabled = PJ2BOOL(ua_cfg.enable_unsolicited_mwi);
+}
+
+pjsua_config UaConfig::toPj() const
+{
+    unsigned i;
+    pjsua_config pua_cfg;
+
+    pjsua_config_default(&pua_cfg);
+
+    pua_cfg.max_calls = this->maxCalls;
+    pua_cfg.thread_cnt = this->threadCnt;
+    pua_cfg.user_agent = str2Pj(this->userAgent);
+
+    for (i=0; i<this->nameserver.size() && i<PJ_ARRAY_SIZE(pua_cfg.nameserver);
+	 ++i)
+    {
+	pua_cfg.nameserver[i] = str2Pj(this->nameserver[i]);
+    }
+    pua_cfg.nameserver_count = i;
+
+    for (i=0; i<this->stunServer.size() && i<PJ_ARRAY_SIZE(pua_cfg.stun_srv);
+	 ++i)
+    {
+	pua_cfg.stun_srv[i] = str2Pj(this->stunServer[i]);
+    }
+    pua_cfg.stun_srv_cnt = i;
+
+    pua_cfg.nat_type_in_sdp = this->natTypeInSdp;
+    pua_cfg.enable_unsolicited_mwi = this->mwiUnsolicitedEnabled;
+
+    return pua_cfg;
+}
+
+void UaConfig::readObject(const ContainerNode &node) throw(Error)
+{
+    ContainerNode this_node = node.readContainer("UaConfig");
+
+    NODE_READ_UNSIGNED( this_node, maxCalls);
+    NODE_READ_UNSIGNED( this_node, threadCnt);
+    NODE_READ_BOOL    ( this_node, mainThreadOnly);
+    NODE_READ_STRINGV ( this_node, nameserver);
+    NODE_READ_STRING  ( this_node, userAgent);
+    NODE_READ_STRINGV ( this_node, stunServer);
+    NODE_READ_BOOL    ( this_node, stunIgnoreFailure);
+    NODE_READ_INT     ( this_node, natTypeInSdp);
+    NODE_READ_BOOL    ( this_node, mwiUnsolicitedEnabled);
+}
+
+void UaConfig::writeObject(ContainerNode &node) const throw(Error)
+{
+    ContainerNode this_node = node.writeNewContainer("UaConfig");
+
+    NODE_WRITE_UNSIGNED( this_node, maxCalls);
+    NODE_WRITE_UNSIGNED( this_node, threadCnt);
+    NODE_WRITE_BOOL    ( this_node, mainThreadOnly);
+    NODE_WRITE_STRINGV ( this_node, nameserver);
+    NODE_WRITE_STRING  ( this_node, userAgent);
+    NODE_WRITE_STRINGV ( this_node, stunServer);
+    NODE_WRITE_BOOL    ( this_node, stunIgnoreFailure);
+    NODE_WRITE_INT     ( this_node, natTypeInSdp);
+    NODE_WRITE_BOOL    ( this_node, mwiUnsolicitedEnabled);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+LogConfig::LogConfig()
+{
+    pjsua_logging_config lc;
+
+    pjsua_logging_config_default(&lc);
+    fromPj(lc);
+}
+
+void LogConfig::fromPj(const pjsua_logging_config &lc)
+{
+    this->msgLogging = lc.msg_logging;
+    this->level = lc.level;
+    this->consoleLevel = lc.console_level;
+    this->decor = lc.decor;
+    this->filename = pj2Str(lc.log_filename);
+    this->fileFlags = lc.log_file_flags;
+    this->writer = NULL;
+}
+
+pjsua_logging_config LogConfig::toPj() const
+{
+    pjsua_logging_config lc;
+
+    pjsua_logging_config_default(&lc);
+
+    lc.msg_logging = this->msgLogging;
+    lc.level = this->level;
+    lc.console_level = this->consoleLevel;
+    lc.decor = this->decor;
+    lc.log_file_flags = this->fileFlags;
+    lc.log_filename = str2Pj(this->filename);
+
+    return lc;
+}
+
+void LogConfig::readObject(const ContainerNode &node) throw(Error)
+{
+    ContainerNode this_node = node.readContainer("LogConfig");
+
+    NODE_READ_UNSIGNED( this_node, msgLogging);
+    NODE_READ_UNSIGNED( this_node, level);
+    NODE_READ_UNSIGNED( this_node, consoleLevel);
+    NODE_READ_UNSIGNED( this_node, decor);
+    NODE_READ_STRING  ( this_node, filename);
+    NODE_READ_UNSIGNED( this_node, fileFlags);
+}
+
+void LogConfig::writeObject(ContainerNode &node) const throw(Error)
+{
+    ContainerNode this_node = node.writeNewContainer("LogConfig");
+
+    NODE_WRITE_UNSIGNED( this_node, msgLogging);
+    NODE_WRITE_UNSIGNED( this_node, level);
+    NODE_WRITE_UNSIGNED( this_node, consoleLevel);
+    NODE_WRITE_UNSIGNED( this_node, decor);
+    NODE_WRITE_STRING  ( this_node, filename);
+    NODE_WRITE_UNSIGNED( this_node, fileFlags);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+MediaConfig::MediaConfig()
+{
+    pjsua_media_config mc;
+
+    pjsua_media_config_default(&mc);
+    fromPj(mc);
+}
+
+void MediaConfig::fromPj(const pjsua_media_config &mc)
+{
+    this->clockRate = mc.clock_rate;
+    this->sndClockRate = mc.snd_clock_rate;
+    this->channelCount = mc.channel_count;
+    this->audioFramePtime = mc.audio_frame_ptime;
+    this->maxMediaPorts = mc.max_media_ports;
+    this->hasIoqueue = PJ2BOOL(mc.has_ioqueue);
+    this->threadCnt = mc.thread_cnt;
+    this->quality = mc.quality;
+    this->ptime = mc.ptime;
+    this->noVad = PJ2BOOL(mc.no_vad);
+    this->ilbcMode = mc.ilbc_mode;
+    this->txDropPct = mc.tx_drop_pct;
+    this->rxDropPct = mc.rx_drop_pct;
+    this->ecOptions = mc.ec_options;
+    this->ecTailLen = mc.ec_tail_len;
+    this->sndRecLatency = mc.snd_rec_latency;
+    this->sndPlayLatency = mc.snd_play_latency;
+    this->jbInit = mc.jb_init;
+    this->jbMinPre = mc.jb_min_pre;
+    this->jbMaxPre = mc.jb_max_pre;
+    this->jbMax = mc.jb_max;
+    this->sndAutoCloseTime = mc.snd_auto_close_time;
+    this->vidPreviewEnableNative = PJ2BOOL(mc.vid_preview_enable_native);
+}
+
+pjsua_media_config MediaConfig::toPj() const
+{
+    pjsua_media_config mcfg;
+
+    pjsua_media_config_default(&mcfg);
+
+    mcfg.clock_rate = this->clockRate;
+    mcfg.snd_clock_rate = this->sndClockRate;
+    mcfg.channel_count = this->channelCount;
+    mcfg.audio_frame_ptime = this->audioFramePtime;
+    mcfg.max_media_ports = this->maxMediaPorts;
+    mcfg.has_ioqueue = this->hasIoqueue;
+    mcfg.thread_cnt = this->threadCnt;
+    mcfg.quality = this->quality;
+    mcfg.ptime = this->ptime;
+    mcfg.no_vad = this->noVad;
+    mcfg.ilbc_mode = this->ilbcMode;
+    mcfg.tx_drop_pct = this->txDropPct;
+    mcfg.rx_drop_pct = this->rxDropPct;
+    mcfg.ec_options = this->ecOptions;
+    mcfg.ec_tail_len = this->ecTailLen;
+    mcfg.snd_rec_latency = this->sndRecLatency;
+    mcfg.snd_play_latency = this->sndPlayLatency;
+    mcfg.jb_init = this->jbInit;
+    mcfg.jb_min_pre = this->jbMinPre;
+    mcfg.jb_max_pre = this->jbMaxPre;
+    mcfg.jb_max = this->jbMax;
+    mcfg.snd_auto_close_time = this->sndAutoCloseTime;
+    mcfg.vid_preview_enable_native = this->vidPreviewEnableNative;
+
+    return mcfg;
+}
+
+void MediaConfig::readObject(const ContainerNode &node) throw(Error)
+{
+    ContainerNode this_node = node.readContainer("MediaConfig");
+
+    NODE_READ_UNSIGNED( this_node, clockRate);
+    NODE_READ_UNSIGNED( this_node, sndClockRate);
+    NODE_READ_UNSIGNED( this_node, channelCount);
+    NODE_READ_UNSIGNED( this_node, audioFramePtime);
+    NODE_READ_UNSIGNED( this_node, maxMediaPorts);
+    NODE_READ_BOOL    ( this_node, hasIoqueue);
+    NODE_READ_UNSIGNED( this_node, threadCnt);
+    NODE_READ_UNSIGNED( this_node, quality);
+    NODE_READ_UNSIGNED( this_node, ptime);
+    NODE_READ_BOOL    ( this_node, noVad);
+    NODE_READ_UNSIGNED( this_node, ilbcMode);
+    NODE_READ_UNSIGNED( this_node, txDropPct);
+    NODE_READ_UNSIGNED( this_node, rxDropPct);
+    NODE_READ_UNSIGNED( this_node, ecOptions);
+    NODE_READ_UNSIGNED( this_node, ecTailLen);
+    NODE_READ_UNSIGNED( this_node, sndRecLatency);
+    NODE_READ_UNSIGNED( this_node, sndPlayLatency);
+    NODE_READ_INT     ( this_node, jbInit);
+    NODE_READ_INT     ( this_node, jbMinPre);
+    NODE_READ_INT     ( this_node, jbMaxPre);
+    NODE_READ_INT     ( this_node, jbMax);
+    NODE_READ_INT     ( this_node, sndAutoCloseTime);
+    NODE_READ_BOOL    ( this_node, vidPreviewEnableNative);
+}
+
+void MediaConfig::writeObject(ContainerNode &node) const throw(Error)
+{
+    ContainerNode this_node = node.writeNewContainer("MediaConfig");
+
+    NODE_WRITE_UNSIGNED( this_node, clockRate);
+    NODE_WRITE_UNSIGNED( this_node, sndClockRate);
+    NODE_WRITE_UNSIGNED( this_node, channelCount);
+    NODE_WRITE_UNSIGNED( this_node, audioFramePtime);
+    NODE_WRITE_UNSIGNED( this_node, maxMediaPorts);
+    NODE_WRITE_BOOL    ( this_node, hasIoqueue);
+    NODE_WRITE_UNSIGNED( this_node, threadCnt);
+    NODE_WRITE_UNSIGNED( this_node, quality);
+    NODE_WRITE_UNSIGNED( this_node, ptime);
+    NODE_WRITE_BOOL    ( this_node, noVad);
+    NODE_WRITE_UNSIGNED( this_node, ilbcMode);
+    NODE_WRITE_UNSIGNED( this_node, txDropPct);
+    NODE_WRITE_UNSIGNED( this_node, rxDropPct);
+    NODE_WRITE_UNSIGNED( this_node, ecOptions);
+    NODE_WRITE_UNSIGNED( this_node, ecTailLen);
+    NODE_WRITE_UNSIGNED( this_node, sndRecLatency);
+    NODE_WRITE_UNSIGNED( this_node, sndPlayLatency);
+    NODE_WRITE_INT     ( this_node, jbInit);
+    NODE_WRITE_INT     ( this_node, jbMinPre);
+    NODE_WRITE_INT     ( this_node, jbMaxPre);
+    NODE_WRITE_INT     ( this_node, jbMax);
+    NODE_WRITE_INT     ( this_node, sndAutoCloseTime);
+    NODE_WRITE_BOOL    ( this_node, vidPreviewEnableNative);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void EpConfig::readObject(const ContainerNode &node) throw(Error)
+{
+    ContainerNode this_node = node.readContainer("EpConfig");
+    NODE_READ_OBJ( this_node, uaConfig);
+    NODE_READ_OBJ( this_node, logConfig);
+    NODE_READ_OBJ( this_node, medConfig);
+}
+
+void EpConfig::writeObject(ContainerNode &node) const throw(Error)
+{
+    ContainerNode this_node = node.writeNewContainer("EpConfig");
+    NODE_WRITE_OBJ( this_node, uaConfig);
+    NODE_WRITE_OBJ( this_node, logConfig);
+    NODE_WRITE_OBJ( this_node, medConfig);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+/* Class to post log to main thread */
+struct PendingLog : public PendingJob
+{
+    LogEntry entry;
+    virtual void execute(bool is_pending)
+    {
+	PJ_UNUSED_ARG(is_pending);
+	Endpoint::instance().utilLogWrite(entry);
+    }
+};
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * Endpoint instance
+ */
+Endpoint::Endpoint()
+: writer(NULL), mainThreadOnly(false), mainThread(NULL), pendingJobSize(0)
+{
+    if (instance_) {
+	PJSUA2_RAISE_ERROR(PJ_EEXISTS);
+    }
+
+    instance_ = this;
+}
+
+Endpoint& Endpoint::instance() throw(Error)
+{
+    if (!instance_) {
+	PJSUA2_RAISE_ERROR(PJ_ENOTFOUND);
+    }
+    return *instance_;
+}
+
+Endpoint::~Endpoint()
+{
+    while (!pendingJobs.empty()) {
+	delete pendingJobs.front();
+	pendingJobs.pop_front();
+    }
+
+    while(mediaList.size() > 0) {
+	AudioMedia *cur_media = mediaList[0];
+	delete cur_media; /* this will remove itself from the list */
+    }
+
+    clearCodecInfoList();
+
+    try {
+	libDestroy();
+    } catch (Error &err) {
+	// Ignore
+	PJ_UNUSED_ARG(err);
+    }
+
+    instance_ = NULL;
+}
+
+void Endpoint::utilAddPendingJob(PendingJob *job)
+{
+    enum {
+	MAX_PENDING_JOBS = 1024
+    };
+
+    /* See if we can execute immediately */
+    if (!mainThreadOnly || pj_thread_this()==mainThread) {
+	job->execute(false);
+	delete job;
+	return;
+    }
+
+    if (pendingJobSize > MAX_PENDING_JOBS) {
+	enum { NUMBER_TO_DISCARD = 5 };
+
+	pj_enter_critical_section();
+	for (unsigned i=0; i<NUMBER_TO_DISCARD; ++i) {
+	    delete pendingJobs.back();
+	    pendingJobs.pop_back();
+	}
+
+	pendingJobSize -= NUMBER_TO_DISCARD;
+	pj_leave_critical_section();
+
+	utilLogWrite(1, THIS_FILE,
+	             "*** ERROR: Job queue full!! Jobs discarded!!! ***");
+    }
+
+    pj_enter_critical_section();
+    pendingJobs.push_back(job);
+    pendingJobSize++;
+    pj_leave_critical_section();
+}
+
+/* Handle log callback */
+void Endpoint::utilLogWrite(LogEntry &entry)
+{
+    if (mainThreadOnly && pj_thread_this() != mainThread) {
+	PendingLog *job = new PendingLog;
+	job->entry = entry;
+	utilAddPendingJob(job);
+    } else {
+	writer->write(entry);
+    }
+}
+
+/* Run pending jobs only in main thread */
+void Endpoint::performPendingJobs()
+{
+    if (pj_thread_this() != mainThread)
+	return;
+
+    if (pendingJobSize == 0)
+	return;
+
+    for (;;) {
+	PendingJob *job = NULL;
+
+	pj_enter_critical_section();
+	if (pendingJobSize != 0) {
+	    job = pendingJobs.front();
+	    pendingJobs.pop_front();
+	    pendingJobSize--;
+	}
+	pj_leave_critical_section();
+
+	if (job) {
+	    job->execute(true);
+	    delete job;
+	} else
+	    break;
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * Endpoint static callbacks
+ */
+void Endpoint::logFunc(int level, const char *data, int len)
+{
+    Endpoint &ep = Endpoint::instance();
+
+    if (!ep.writer)
+	return;
+
+    LogEntry entry;
+    entry.level = level;
+    entry.msg = string(data, len);
+    entry.threadId = (long)pj_thread_this();
+    entry.threadName = string(pj_thread_get_name(pj_thread_this()));
+
+    ep.utilLogWrite(entry);
+}
+
+void Endpoint::stun_resolve_cb(const pj_stun_resolve_result *res)
+{
+    Endpoint &ep = Endpoint::instance();
+
+    if (!res)
+	return;
+
+    OnNatCheckStunServersCompleteParam prm;
+
+    prm.userData = res->token;
+    prm.status = res->status;
+    if (res->status == PJ_SUCCESS) {
+	char straddr[PJ_INET6_ADDRSTRLEN+10];
+
+	prm.name = string(res->name.ptr, res->name.slen);
+	pj_sockaddr_print(&res->addr, straddr, sizeof(straddr), 3);
+	prm.addr = straddr;
+    }
+
+    ep.onNatCheckStunServersComplete(prm);
+}
+
+void Endpoint::on_timer(pj_timer_heap_t *timer_heap,
+                        pj_timer_entry *entry)
+{
+    PJ_UNUSED_ARG(timer_heap);
+
+    Endpoint &ep = Endpoint::instance();
+    UserTimer *ut = (UserTimer*) entry->user_data;
+
+    if (ut->signature != TIMER_SIGNATURE)
+	return;
+
+    ep.onTimer(ut->prm);
+}
+
+void Endpoint::on_nat_detect(const pj_stun_nat_detect_result *res)
+{
+    Endpoint &ep = Endpoint::instance();
+
+    if (!res)
+	return;
+
+    OnNatDetectionCompleteParam prm;
+
+    prm.status = res->status;
+    prm.reason = res->status_text;
+    prm.natType = res->nat_type;
+    prm.natTypeName = res->nat_type_name;
+
+    ep.onNatDetectionComplete(prm);
+}
+
+void Endpoint::on_transport_state( pjsip_transport *tp,
+				   pjsip_transport_state state,
+				   const pjsip_transport_state_info *info)
+{
+    Endpoint &ep = Endpoint::instance();
+
+    OnTransportStateParam prm;
+
+    prm.hnd = (TransportHandle)tp;
+    prm.state = state;
+    prm.lastError = info ? info->status : PJ_SUCCESS;
+
+    ep.onTransportState(prm);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * Account static callbacks
+ */
+
+Account *Endpoint::lookupAcc(int acc_id, const char *op)
+{
+    Account *acc = Account::lookup(acc_id);
+    if (!acc) {
+	PJ_LOG(1,(THIS_FILE,
+		  "Error: cannot find Account instance for account id %d in "
+		  "%s", acc_id, op));
+    }
+
+    return acc;
+}
+
+Call *Endpoint::lookupCall(int call_id, const char *op)
+{
+    Call *call = Call::lookup(call_id);
+    if (!call) {
+	PJ_LOG(1,(THIS_FILE,
+		  "Error: cannot find Call instance for call id %d in "
+		  "%s", call_id, op));
+    }
+
+    return call;
+}
+
+void Endpoint::on_incoming_call(pjsua_acc_id acc_id, pjsua_call_id call_id,
+                                pjsip_rx_data *rdata)
+{
+    Account *acc = lookupAcc(acc_id, "on_incoming_call()");
+    if (!acc) {
+	pjsua_call_hangup(call_id, PJSIP_SC_INTERNAL_SERVER_ERROR, NULL, NULL);
+	return;
+    }
+
+    /* call callback */
+    OnIncomingCallParam prm;
+    prm.callId = call_id;
+    prm.rdata.fromPj(*rdata);
+
+    acc->onIncomingCall(prm);
+
+    /* disconnect if callback doesn't handle the call */
+    pjsua_call_info ci;
+
+    pjsua_call_get_info(call_id, &ci);
+    if (!pjsua_call_get_user_data(call_id) &&
+	ci.state != PJSIP_INV_STATE_DISCONNECTED)
+    {
+	pjsua_call_hangup(call_id, PJSIP_SC_INTERNAL_SERVER_ERROR, NULL, NULL);
+    }
+}
+
+void Endpoint::on_reg_started(pjsua_acc_id acc_id, pj_bool_t renew)
+{
+    Account *acc = lookupAcc(acc_id, "on_reg_started()");
+    if (!acc) {
+	return;
+    }
+
+    OnRegStartedParam prm;
+    prm.renew = PJ2BOOL(renew);
+    acc->onRegStarted(prm);
+}
+
+void Endpoint::on_reg_state2(pjsua_acc_id acc_id, pjsua_reg_info *info)
+{
+    Account *acc = lookupAcc(acc_id, "on_reg_state2()");
+    if (!acc) {
+	return;
+    }
+
+    OnRegStateParam prm;
+    prm.status		= info->cbparam->status;
+    prm.code 		= (pjsip_status_code) info->cbparam->code;
+    prm.reason		= pj2Str(info->cbparam->reason);
+    if (info->cbparam->rdata)
+	prm.rdata.fromPj(*info->cbparam->rdata);
+    prm.expiration	= info->cbparam->expiration;
+
+    acc->onRegState(prm);
+}
+
+void Endpoint::on_incoming_subscribe(pjsua_acc_id acc_id,
+                                     pjsua_srv_pres *srv_pres,
+                                     pjsua_buddy_id buddy_id,
+                                     const pj_str_t *from,
+                                     pjsip_rx_data *rdata,
+                                     pjsip_status_code *code,
+                                     pj_str_t *reason,
+                                     pjsua_msg_data *msg_data)
+{
+    PJ_UNUSED_ARG(buddy_id);
+    PJ_UNUSED_ARG(srv_pres);
+
+    Account *acc = lookupAcc(acc_id, "on_incoming_subscribe()");
+    if (!acc) {
+	/* default behavior should apply */
+	return;
+    }
+
+    OnIncomingSubscribeParam prm;
+    prm.srvPres		= srv_pres;
+    prm.fromUri 	= pj2Str(*from);
+    prm.rdata.fromPj(*rdata);
+    prm.code		= *code;
+    prm.reason		= pj2Str(*reason);
+    prm.txOption.fromPj(*msg_data);
+
+    acc->onIncomingSubscribe(prm);
+
+    *code = prm.code;
+    acc->tmpReason = prm.reason;
+    *reason = str2Pj(acc->tmpReason);
+    prm.txOption.toPj(*msg_data);
+}
+
+void Endpoint::on_pager2(pjsua_call_id call_id,
+                         const pj_str_t *from,
+                         const pj_str_t *to,
+                         const pj_str_t *contact,
+                         const pj_str_t *mime_type,
+                         const pj_str_t *body,
+                         pjsip_rx_data *rdata,
+                         pjsua_acc_id acc_id)
+{
+    OnInstantMessageParam prm;
+    prm.fromUri		= pj2Str(*from);
+    prm.toUri		= pj2Str(*to);
+    prm.contactUri	= pj2Str(*contact);
+    prm.contentType	= pj2Str(*mime_type);
+    prm.msgBody		= pj2Str(*body);
+    prm.rdata.fromPj(*rdata);
+
+    if (call_id != PJSUA_INVALID_ID) {
+	Call *call = lookupCall(call_id, "on_pager2()");
+	if (!call) {
+	    /* Ignored */
+	    return;
+	}
+
+	call->onInstantMessage(prm);
+    } else {
+	Account *acc = lookupAcc(acc_id, "on_pager2()");
+	if (!acc) {
+	    /* Ignored */
+	    return;
+	}
+
+	acc->onInstantMessage(prm);
+    }
+}
+
+void Endpoint::on_pager_status2( pjsua_call_id call_id,
+				 const pj_str_t *to,
+				 const pj_str_t *body,
+				 void *user_data,
+				 pjsip_status_code status,
+				 const pj_str_t *reason,
+				 pjsip_tx_data *tdata,
+				 pjsip_rx_data *rdata,
+				 pjsua_acc_id acc_id)
+{
+    PJ_UNUSED_ARG(tdata);
+
+    OnInstantMessageStatusParam prm;
+    prm.userData	= user_data;
+    prm.toUri		= pj2Str(*to);
+    prm.msgBody		= pj2Str(*body);
+    prm.code		= status;
+    prm.reason		= pj2Str(*reason);
+    if (rdata)
+	prm.rdata.fromPj(*rdata);
+
+    if (call_id != PJSUA_INVALID_ID) {
+	Call *call = lookupCall(call_id, "on_pager_status2()");
+	if (!call) {
+	    /* Ignored */
+	    return;
+	}
+
+	call->onInstantMessageStatus(prm);
+    } else {
+	Account *acc = lookupAcc(acc_id, "on_pager_status2()");
+	if (!acc) {
+	    /* Ignored */
+	    return;
+	}
+
+	acc->onInstantMessageStatus(prm);
+    }
+}
+
+void Endpoint::on_typing2( pjsua_call_id call_id,
+			   const pj_str_t *from,
+			   const pj_str_t *to,
+			   const pj_str_t *contact,
+			   pj_bool_t is_typing,
+			   pjsip_rx_data *rdata,
+			   pjsua_acc_id acc_id)
+{
+    OnTypingIndicationParam prm;
+    prm.fromUri		= pj2Str(*from);
+    prm.toUri		= pj2Str(*to);
+    prm.contactUri	= pj2Str(*contact);
+    prm.isTyping	= is_typing != 0;
+    prm.rdata.fromPj(*rdata);
+
+    if (call_id != PJSUA_INVALID_ID) {
+	Call *call = lookupCall(call_id, "on_typing2()");
+	if (!call) {
+	    /* Ignored */
+	    return;
+	}
+
+	call->onTypingIndication(prm);
+    } else {
+	Account *acc = lookupAcc(acc_id, "on_typing2()");
+	if (!acc) {
+	    /* Ignored */
+	    return;
+	}
+
+	acc->onTypingIndication(prm);
+    }
+}
+
+void Endpoint::on_mwi_info(pjsua_acc_id acc_id,
+                           pjsua_mwi_info *mwi_info)
+{
+    OnMwiInfoParam prm;
+    prm.state	= pjsip_evsub_get_state(mwi_info->evsub);
+    prm.rdata.fromPj(*mwi_info->rdata);
+
+    Account *acc = lookupAcc(acc_id, "on_mwi_info()");
+    if (!acc) {
+	/* Ignored */
+	return;
+    }
+
+    acc->onMwiInfo(prm);
+}
+
+void Endpoint::on_buddy_state(pjsua_buddy_id buddy_id)
+{
+    Buddy *buddy = (Buddy*)pjsua_buddy_get_user_data(buddy_id);
+    if (!buddy || !buddy->isValid()) {
+	/* Ignored */
+	return;
+    }
+
+    buddy->onBuddyState();
+}
+
+// Call callbacks
+void Endpoint::on_call_state(pjsua_call_id call_id, pjsip_event *e)
+{
+    Call *call = Call::lookup(call_id);
+    if (!call) {
+	return;
+    }
+    
+    OnCallStateParam prm;
+    prm.e.fromPj(*e);
+    
+    call->processStateChange(prm);
+    /* If the state is DISCONNECTED, call may have already been deleted
+     * by the application in the callback, so do not access it anymore here.
+     */
+}
+
+void Endpoint::on_call_tsx_state(pjsua_call_id call_id,
+                                 pjsip_transaction *tsx,
+                                 pjsip_event *e)
+{
+    PJ_UNUSED_ARG(tsx);
+
+    Call *call = Call::lookup(call_id);
+    if (!call) {
+	return;
+    }
+    
+    OnCallTsxStateParam prm;
+    prm.e.fromPj(*e);
+    
+    call->onCallTsxState(prm);
+}
+
+void Endpoint::on_call_media_state(pjsua_call_id call_id)
+{
+    Call *call = Call::lookup(call_id);
+    if (!call) {
+	return;
+    }
+
+    OnCallMediaStateParam prm;
+    call->processMediaUpdate(prm);
+}
+
+void Endpoint::on_call_sdp_created(pjsua_call_id call_id,
+                                   pjmedia_sdp_session *sdp,
+                                   pj_pool_t *pool,
+                                   const pjmedia_sdp_session *rem_sdp)
+{
+    Call *call = Call::lookup(call_id);
+    if (!call) {
+	return;
+    }
+    
+    OnCallSdpCreatedParam prm;
+    string orig_sdp;
+    
+    prm.sdp.fromPj(*sdp);
+    orig_sdp = prm.sdp.wholeSdp;
+    if (rem_sdp)
+        prm.remSdp.fromPj(*rem_sdp);
+    
+    call->onCallSdpCreated(prm);
+    
+    /* Check if application modifies the SDP */
+    if (orig_sdp != prm.sdp.wholeSdp) {
+        pjmedia_sdp_parse(pool, (char*)prm.sdp.wholeSdp.c_str(),
+                          prm.sdp.wholeSdp.size(), &sdp);
+    }
+}
+
+void Endpoint::on_stream_created(pjsua_call_id call_id,
+                                 pjmedia_stream *strm,
+                                 unsigned stream_idx,
+                                 pjmedia_port **p_port)
+{
+    Call *call = Call::lookup(call_id);
+    if (!call) {
+	return;
+    }
+    
+    OnStreamCreatedParam prm;
+    prm.stream = strm;
+    prm.streamIdx = stream_idx;
+    prm.pPort = (void *)*p_port;
+    
+    call->onStreamCreated(prm);
+    
+    if (prm.pPort != (void *)*p_port)
+        *p_port = (pjmedia_port *)prm.pPort;
+}
+
+void Endpoint::on_stream_destroyed(pjsua_call_id call_id,
+                                   pjmedia_stream *strm,
+                                   unsigned stream_idx)
+{
+    Call *call = Call::lookup(call_id);
+    if (!call) {
+	return;
+    }
+    
+    OnStreamDestroyedParam prm;
+    prm.stream = strm;
+    prm.streamIdx = stream_idx;
+    
+    call->onStreamDestroyed(prm);
+}
+
+struct PendingOnDtmfDigitCallback : public PendingJob
+{
+    int call_id;
+    OnDtmfDigitParam prm;
+
+    virtual void execute(bool is_pending)
+    {
+	PJ_UNUSED_ARG(is_pending);
+
+	Call *call = Call::lookup(call_id);
+	if (!call)
+	    return;
+
+	call->onDtmfDigit(prm);
+    }
+};
+
+void Endpoint::on_dtmf_digit(pjsua_call_id call_id, int digit)
+{
+    Call *call = Call::lookup(call_id);
+    if (!call) {
+	return;
+    }
+    
+    PendingOnDtmfDigitCallback *job = new PendingOnDtmfDigitCallback;
+    job->call_id = call_id;
+    char buf[10];
+    pj_ansi_sprintf(buf, "%c", digit);
+    job->prm.digit = (string)buf;
+    
+    Endpoint::instance().utilAddPendingJob(job);
+}
+
+void Endpoint::on_call_transfer_request2(pjsua_call_id call_id,
+                                         const pj_str_t *dst,
+                                         pjsip_status_code *code,
+                                         pjsua_call_setting *opt)
+{
+    Call *call = Call::lookup(call_id);
+    if (!call) {
+	return;
+    }
+    
+    OnCallTransferRequestParam prm;
+    prm.dstUri = pj2Str(*dst);
+    prm.statusCode = *code;
+    prm.opt.fromPj(*opt);
+    
+    call->onCallTransferRequest(prm);
+    
+    *code = prm.statusCode;
+    *opt = prm.opt.toPj();
+}
+
+void Endpoint::on_call_transfer_status(pjsua_call_id call_id,
+                                       int st_code,
+                                       const pj_str_t *st_text,
+                                       pj_bool_t final,
+                                       pj_bool_t *p_cont)
+{
+    Call *call = Call::lookup(call_id);
+    if (!call) {
+	return;
+    }
+    
+    OnCallTransferStatusParam prm;
+    prm.statusCode = (pjsip_status_code)st_code;
+    prm.reason = pj2Str(*st_text);
+    prm.finalNotify = PJ2BOOL(final);
+    prm.cont = PJ2BOOL(*p_cont);
+    
+    call->onCallTransferStatus(prm);
+    
+    *p_cont = prm.cont;
+}
+
+void Endpoint::on_call_replace_request2(pjsua_call_id call_id,
+                                        pjsip_rx_data *rdata,
+                                        int *st_code,
+                                        pj_str_t *st_text,
+                                        pjsua_call_setting *opt)
+{
+    Call *call = Call::lookup(call_id);
+    if (!call) {
+	return;
+    }
+    
+    OnCallReplaceRequestParam prm;
+    prm.rdata.fromPj(*rdata);
+    prm.statusCode = (pjsip_status_code)*st_code;
+    prm.reason = pj2Str(*st_text);
+    prm.opt.fromPj(*opt);
+    
+    call->onCallReplaceRequest(prm);
+    
+    *st_code = prm.statusCode;
+    *st_text = str2Pj(prm.reason);
+    *opt = prm.opt.toPj();
+}
+
+void Endpoint::on_call_replaced(pjsua_call_id old_call_id,
+                                pjsua_call_id new_call_id)
+{
+    Call *call = Call::lookup(old_call_id);
+    if (!call) {
+	return;
+    }
+    
+    OnCallReplacedParam prm;
+    prm.newCallId = new_call_id;
+    
+    call->onCallReplaced(prm);
+}
+
+void Endpoint::on_call_rx_offer(pjsua_call_id call_id,
+                                const pjmedia_sdp_session *offer,
+                                void *reserved,
+                                pjsip_status_code *code,
+                                pjsua_call_setting *opt)
+{
+    PJ_UNUSED_ARG(reserved);
+
+    Call *call = Call::lookup(call_id);
+    if (!call) {
+	return;
+    }
+    
+    OnCallRxOfferParam prm;
+    prm.offer.fromPj(*offer);
+    prm.statusCode = *code;
+    prm.opt.fromPj(*opt);
+    
+    call->onCallRxOffer(prm);
+    
+    *code = prm.statusCode;
+    *opt = prm.opt.toPj();
+}
+
+pjsip_redirect_op Endpoint::on_call_redirected(pjsua_call_id call_id,
+                                               const pjsip_uri *target,
+                                               const pjsip_event *e)
+{
+    Call *call = Call::lookup(call_id);
+    if (!call) {
+	return PJSIP_REDIRECT_STOP;
+    }
+    
+    OnCallRedirectedParam prm;
+    char uristr[PJSIP_MAX_URL_SIZE];
+    int len = pjsip_uri_print(PJSIP_URI_IN_FROMTO_HDR, target, uristr,
+                              sizeof(uristr));
+    if (len < 1) {
+        pj_ansi_strcpy(uristr, "--URI too long--");
+    }
+    prm.targetUri = string(uristr);
+    if (e)
+        prm.e.fromPj(*e);
+    else
+        prm.e.type = PJSIP_EVENT_UNKNOWN;
+    
+    return call->onCallRedirected(prm);
+}
+
+
+struct PendingOnMediaTransportCallback : public PendingJob
+{
+    int call_id;
+    OnCallMediaTransportStateParam prm;
+
+    virtual void execute(bool is_pending)
+    {
+	PJ_UNUSED_ARG(is_pending);
+
+	Call *call = Call::lookup(call_id);
+	if (!call)
+	    return;
+
+	call->onCallMediaTransportState(prm);
+    }
+};
+
+pj_status_t
+Endpoint::on_call_media_transport_state(pjsua_call_id call_id,
+                                        const pjsua_med_tp_state_info *info)
+{
+    Call *call = Call::lookup(call_id);
+    if (!call) {
+	return PJ_SUCCESS;
+    }
+
+    PendingOnMediaTransportCallback *job = new PendingOnMediaTransportCallback;
+    
+    job->call_id = call_id;
+    job->prm.medIdx = info->med_idx;
+    job->prm.state = info->state;
+    job->prm.status = info->status;
+    job->prm.sipErrorCode = info->sip_err_code;
+    
+    Endpoint::instance().utilAddPendingJob(job);
+
+    return PJ_SUCCESS;
+}
+
+struct PendingOnMediaEventCallback : public PendingJob
+{
+    int call_id;
+    OnCallMediaEventParam prm;
+
+    virtual void execute(bool is_pending)
+    {
+	Call *call = Call::lookup(call_id);
+	if (!call)
+	    return;
+
+	if (is_pending) {
+	    /* Can't do this anymore, pointer is invalid */
+	    prm.ev.pjMediaEvent = NULL;
+	}
+
+	call->onCallMediaEvent(prm);
+    }
+};
+
+void Endpoint::on_call_media_event(pjsua_call_id call_id,
+                                   unsigned med_idx,
+                                   pjmedia_event *event)
+{
+    Call *call = Call::lookup(call_id);
+    if (!call) {
+	return;
+    }
+    
+    PendingOnMediaEventCallback *job = new PendingOnMediaEventCallback;
+
+    job->call_id = call_id;
+    job->prm.medIdx = med_idx;
+    job->prm.ev.fromPj(*event);
+    
+    Endpoint::instance().utilAddPendingJob(job);
+}
+
+pjmedia_transport*
+Endpoint::on_create_media_transport(pjsua_call_id call_id,
+                                    unsigned media_idx,
+                                    pjmedia_transport *base_tp,
+                                    unsigned flags)
+{
+    Call *call = Call::lookup(call_id);
+    if (!call) {
+	return base_tp;
+    }
+    
+    OnCreateMediaTransportParam prm;
+    prm.mediaIdx = media_idx;
+    prm.mediaTp = base_tp;
+    prm.flags = flags;
+    
+    call->onCreateMediaTransport(prm);
+    
+    return (pjmedia_transport *)prm.mediaTp;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * Endpoint library operations
+ */
+Version Endpoint::libVersion() const
+{
+    Version ver;
+    ver.major = PJ_VERSION_NUM_MAJOR;
+    ver.minor = PJ_VERSION_NUM_MINOR;
+    ver.rev = PJ_VERSION_NUM_REV;
+    ver.suffix = PJ_VERSION_NUM_EXTRA;
+    ver.full = pj_get_version();
+    ver.numeric = PJ_VERSION_NUM;
+    return ver;
+}
+
+void Endpoint::libCreate() throw(Error)
+{
+    PJSUA2_CHECK_EXPR( pjsua_create() );
+    mainThread = pj_thread_this();
+}
+
+pjsua_state Endpoint::libGetState() const
+{
+    return pjsua_get_state();
+}
+
+void Endpoint::libInit(const EpConfig &prmEpConfig) throw(Error)
+{
+    pjsua_config ua_cfg;
+    pjsua_logging_config log_cfg;
+    pjsua_media_config med_cfg;
+
+    ua_cfg = prmEpConfig.uaConfig.toPj();
+    log_cfg = prmEpConfig.logConfig.toPj();
+    med_cfg = prmEpConfig.medConfig.toPj();
+
+    /* Setup log callback */
+    if (prmEpConfig.logConfig.writer) {
+	this->writer = prmEpConfig.logConfig.writer;
+	log_cfg.cb = &Endpoint::logFunc;
+    }
+    mainThreadOnly = prmEpConfig.uaConfig.mainThreadOnly;
+
+    /* Setup UA callbacks */
+    pj_bzero(&ua_cfg.cb, sizeof(ua_cfg.cb));
+    ua_cfg.cb.on_nat_detect 	= &Endpoint::on_nat_detect;
+    ua_cfg.cb.on_transport_state = &Endpoint::on_transport_state;
+
+    ua_cfg.cb.on_incoming_call	= &Endpoint::on_incoming_call;
+    ua_cfg.cb.on_reg_started	= &Endpoint::on_reg_started;
+    ua_cfg.cb.on_reg_state2	= &Endpoint::on_reg_state2;
+    ua_cfg.cb.on_incoming_subscribe = &Endpoint::on_incoming_subscribe;
+    ua_cfg.cb.on_pager2		= &Endpoint::on_pager2;
+    ua_cfg.cb.on_pager_status2	= &Endpoint::on_pager_status2;
+    ua_cfg.cb.on_typing2	= &Endpoint::on_typing2;
+    ua_cfg.cb.on_mwi_info	= &Endpoint::on_mwi_info;
+    ua_cfg.cb.on_buddy_state	= &Endpoint::on_buddy_state;
+
+    /* Call callbacks */
+    ua_cfg.cb.on_call_state             = &Endpoint::on_call_state;
+    ua_cfg.cb.on_call_tsx_state         = &Endpoint::on_call_tsx_state;
+    ua_cfg.cb.on_call_media_state       = &Endpoint::on_call_media_state;
+    ua_cfg.cb.on_call_sdp_created       = &Endpoint::on_call_sdp_created;
+    ua_cfg.cb.on_stream_created         = &Endpoint::on_stream_created;
+    ua_cfg.cb.on_stream_destroyed       = &Endpoint::on_stream_destroyed;
+    ua_cfg.cb.on_dtmf_digit             = &Endpoint::on_dtmf_digit;
+    ua_cfg.cb.on_call_transfer_request2 = &Endpoint::on_call_transfer_request2;
+    ua_cfg.cb.on_call_transfer_status   = &Endpoint::on_call_transfer_status;
+    ua_cfg.cb.on_call_replace_request2  = &Endpoint::on_call_replace_request2;
+    ua_cfg.cb.on_call_replaced          = &Endpoint::on_call_replaced;
+    ua_cfg.cb.on_call_rx_offer          = &Endpoint::on_call_rx_offer;
+    ua_cfg.cb.on_call_redirected        = &Endpoint::on_call_redirected;
+    ua_cfg.cb.on_call_media_transport_state =
+        &Endpoint::on_call_media_transport_state;
+    ua_cfg.cb.on_call_media_event       = &Endpoint::on_call_media_event;
+    ua_cfg.cb.on_create_media_transport = &Endpoint::on_create_media_transport;
+
+    /* Init! */
+    PJSUA2_CHECK_EXPR( pjsua_init(&ua_cfg, &log_cfg, &med_cfg) );
+}
+
+void Endpoint::libStart() throw(Error)
+{
+    PJSUA2_CHECK_EXPR(pjsua_start());
+}
+
+void Endpoint::libRegisterWorkerThread(const string &name) throw(Error)
+{
+    PJSUA2_CHECK_EXPR(pjsua_register_worker_thread(name.c_str()));
+}
+
+void Endpoint::libStopWorkerThreads()
+{
+    pjsua_stop_worker_threads();
+}
+
+int Endpoint::libHandleEvents(unsigned msec_timeout)
+{
+    performPendingJobs();
+    return pjsua_handle_events(msec_timeout);
+}
+
+void Endpoint::libDestroy(unsigned flags) throw(Error)
+{
+    pj_status_t status;
+
+    status = pjsua_destroy2(flags);
+
+    delete this->writer;
+    this->writer = NULL;
+
+    if (pj_log_get_log_func() == &Endpoint::logFunc) {
+	pj_log_set_log_func(NULL);
+    }
+
+    PJSUA2_CHECK_RAISE_ERROR(status);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * Endpoint Utilities
+ */
+string Endpoint::utilStrError(pj_status_t prmErr)
+{
+    char errmsg[PJ_ERR_MSG_SIZE];
+    pj_strerror(prmErr, errmsg, sizeof(errmsg));
+    return errmsg;
+}
+
+static void ept_log_write(int level, const char *sender,
+                          const char *format, ...)
+{
+    va_list arg;
+    va_start(arg, format);
+    pj_log(sender, level, format, arg );
+    va_end(arg);
+}
+
+void Endpoint::utilLogWrite(int prmLevel,
+			    const string &prmSender,
+			    const string &prmMsg)
+{
+    ept_log_write(prmLevel, prmSender.c_str(), "%s", prmMsg.c_str());
+}
+
+pj_status_t Endpoint::utilVerifySipUri(const string &prmUri)
+{
+    return pjsua_verify_sip_url(prmUri.c_str());
+}
+
+pj_status_t Endpoint::utilVerifyUri(const string &prmUri)
+{
+    return pjsua_verify_url(prmUri.c_str());
+}
+
+Token Endpoint::utilTimerSchedule(unsigned prmMsecDelay,
+                                  Token prmUserData) throw (Error)
+{
+    UserTimer *ut;
+    pj_time_val delay;
+    pj_status_t status;
+
+    ut = new UserTimer;
+    ut->signature = TIMER_SIGNATURE;
+    ut->prm.msecDelay = prmMsecDelay;
+    ut->prm.userData = prmUserData;
+    pj_timer_entry_init(&ut->entry, 1, ut, &Endpoint::on_timer);
+
+    delay.sec = 0;
+    delay.msec = prmMsecDelay;
+    pj_time_val_normalize(&delay);
+
+    status = pjsua_schedule_timer(&ut->entry, &delay);
+    if (status != PJ_SUCCESS) {
+	delete ut;
+	PJSUA2_CHECK_RAISE_ERROR(status);
+    }
+
+    return (Token)ut;
+}
+
+void Endpoint::utilTimerCancel(Token prmTimerToken)
+{
+    UserTimer *ut = (UserTimer*)(void*)prmTimerToken;
+
+    if (ut->signature != TIMER_SIGNATURE) {
+	PJ_LOG(1,(THIS_FILE,
+		  "Invalid timer token in Endpoint::utilTimerCancel()"));
+	return;
+    }
+
+    ut->entry.id = 0;
+    ut->signature = 0xFFFFFFFE;
+    pjsua_cancel_timer(&ut->entry);
+
+    delete ut;
+}
+
+IntVector Endpoint::utilSslGetAvailableCiphers() throw (Error)
+{
+#if PJ_HAS_SSL_SOCK
+    pj_ssl_cipher ciphers[64];
+    unsigned count = PJ_ARRAY_SIZE(ciphers);
+
+    PJSUA2_CHECK_EXPR( pj_ssl_cipher_get_availables(ciphers, &count) );
+
+    return IntVector(ciphers, ciphers + count);
+#else
+    return IntVector();
+#endif
+}
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * Endpoint NAT operations
+ */
+void Endpoint::natDetectType(void) throw(Error)
+{
+    PJSUA2_CHECK_EXPR( pjsua_detect_nat_type() );
+}
+
+pj_stun_nat_type Endpoint::natGetType() throw(Error)
+{
+    pj_stun_nat_type type;
+
+    PJSUA2_CHECK_EXPR( pjsua_get_nat_type(&type) );
+
+    return type;
+}
+
+void Endpoint::natCheckStunServers(const StringVector &servers,
+				   bool wait,
+				   Token token) throw(Error)
+{
+    pj_str_t srv[MAX_STUN_SERVERS];
+    unsigned i, count = 0;
+
+    for (i=0; i<servers.size() && i<MAX_STUN_SERVERS; ++i) {
+	srv[count].ptr = (char*)servers[i].c_str();
+	srv[count].slen = servers[i].size();
+	++count;
+    }
+
+    PJSUA2_CHECK_EXPR(pjsua_resolve_stun_servers(count, srv, wait, token,
+                                                 &Endpoint::stun_resolve_cb) );
+}
+
+void Endpoint::natCancelCheckStunServers(Token token,
+                                         bool notify_cb) throw(Error)
+{
+    PJSUA2_CHECK_EXPR( pjsua_cancel_stun_resolution(token, notify_cb) );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * Transport API
+ */
+TransportId Endpoint::transportCreate(pjsip_transport_type_e type,
+                                      const TransportConfig &cfg) throw(Error)
+{
+    pjsua_transport_config tcfg;
+    pjsua_transport_id tid;
+
+    tcfg = cfg.toPj();
+    PJSUA2_CHECK_EXPR( pjsua_transport_create(type,
+                                              &tcfg, &tid) );
+
+    return tid;
+}
+
+IntVector Endpoint::transportEnum() throw(Error)
+{
+    pjsua_transport_id tids[32];
+    unsigned count = PJ_ARRAY_SIZE(tids);
+
+    PJSUA2_CHECK_EXPR( pjsua_enum_transports(tids, &count) );
+
+    return IntVector(tids, tids+count);
+}
+
+TransportInfo Endpoint::transportGetInfo(TransportId id) throw(Error)
+{
+    pjsua_transport_info pj_tinfo;
+    TransportInfo tinfo;
+
+    PJSUA2_CHECK_EXPR( pjsua_transport_get_info(id, &pj_tinfo) );
+    tinfo.fromPj(pj_tinfo);
+
+    return tinfo;
+}
+
+void Endpoint::transportSetEnable(TransportId id, bool enabled) throw(Error)
+{
+    PJSUA2_CHECK_EXPR( pjsua_transport_set_enable(id, enabled) );
+}
+
+void Endpoint::transportClose(TransportId id) throw(Error)
+{
+    PJSUA2_CHECK_EXPR( pjsua_transport_close(id, PJ_FALSE) );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * Call operations
+ */
+
+void Endpoint::hangupAllCalls(void)
+{
+    pjsua_call_hangup_all();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * Media API
+ */
+unsigned Endpoint::mediaMaxPorts() const
+{
+    return pjsua_conf_get_max_ports();
+}
+
+unsigned Endpoint::mediaActivePorts() const
+{
+    return pjsua_conf_get_active_ports();
+}
+
+const AudioMediaVector &Endpoint::mediaEnumPorts() const throw(Error)
+{
+    return mediaList;
+}
+
+void Endpoint::mediaAdd(AudioMedia &media)
+{
+    if (mediaExists(media))
+	return;
+
+    mediaList.push_back(&media);
+}
+
+void Endpoint::mediaRemove(AudioMedia &media)
+{
+    AudioMediaVector::iterator it = std::find(mediaList.begin(),
+					      mediaList.end(),
+					      &media);
+
+    if (it != mediaList.end())
+	mediaList.erase(it);
+
+}
+
+bool Endpoint::mediaExists(const AudioMedia &media) const
+{
+    AudioMediaVector::const_iterator it = std::find(mediaList.begin(),
+						    mediaList.end(),
+						    &media);
+
+    return (it != mediaList.end());
+}
+
+AudDevManager &Endpoint::audDevManager()
+{
+    return audioDevMgr;
+}
+
+/*
+ * Codec operations.
+ */
+const CodecInfoVector &Endpoint::codecEnum() throw(Error)
+{
+    pjsua_codec_info pj_codec[MAX_CODEC_NUM];
+    unsigned count = 0;
+
+    PJSUA2_CHECK_EXPR( pjsua_enum_codecs(pj_codec, &count) );
+
+    pj_enter_critical_section();
+    clearCodecInfoList();
+    for (unsigned i=0;(i<count && i<MAX_CODEC_NUM);++i) {
+	CodecInfo *codec_info = new CodecInfo;
+
+	codec_info->fromPj(pj_codec[i]);
+	codecInfoList.push_back(codec_info);
+    }
+    pj_leave_critical_section();
+    return codecInfoList;
+}
+
+void Endpoint::codecSetPriority(const string &codec_id,
+			        pj_uint8_t priority) throw(Error)
+{
+    pj_str_t codec_str = str2Pj(codec_id);
+    PJSUA2_CHECK_EXPR( pjsua_codec_set_priority(&codec_str, priority) );
+}
+
+CodecParam Endpoint::codecGetParam(const string &codec_id) const throw(Error)
+{
+    pjmedia_codec_param *pj_param = NULL;
+    pj_str_t codec_str = str2Pj(codec_id);
+
+    PJSUA2_CHECK_EXPR( pjsua_codec_get_param(&codec_str, pj_param) );
+
+    return pj_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;
+
+    PJSUA2_CHECK_EXPR( pjsua_codec_set_param(&codec_str, pj_param) );
+}
+
+void Endpoint::clearCodecInfoList()
+{
+    for (unsigned i=0;i<codecInfoList.size();++i) {
+	delete codecInfoList[i];
+    }
+    codecInfoList.clear();
+}
Index: /pjproject/trunk/pjsip/src/pjsua2/json.cpp
===================================================================
--- /pjproject/trunk/pjsip/src/pjsua2/json.cpp	(revision 4704)
+++ /pjproject/trunk/pjsip/src/pjsua2/json.cpp	(revision 4704)
@@ -0,0 +1,544 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2013 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 <pjsua2/json.hpp>
+#include <pjlib-util/errno.h>
+#include <pj/file_io.h>
+#include "util.hpp"
+
+#define THIS_FILE	"json.cpp"
+
+using namespace pj;
+using namespace std;
+
+/* Json node operations */
+static bool		jsonNode_hasUnread(const ContainerNode*);
+static string		jsonNode_unreadName(const ContainerNode*n)
+				            throw(Error);
+static float		jsonNode_readNumber(const ContainerNode*,
+            		                    const string&)
+					      throw(Error);
+static bool		jsonNode_readBool(const ContainerNode*,
+           		                  const string&)
+					  throw(Error);
+static string		jsonNode_readString(const ContainerNode*,
+             		                    const string&)
+					    throw(Error);
+static StringVector	jsonNode_readStringVector(const ContainerNode*,
+                   	                          const string&)
+						  throw(Error);
+static ContainerNode	jsonNode_readContainer(const ContainerNode*,
+                    	                       const string &)
+					       throw(Error);
+static ContainerNode	jsonNode_readArray(const ContainerNode*,
+                    	                   const string &)
+					   throw(Error);
+static void		jsonNode_writeNumber(ContainerNode*,
+           		                     const string &name,
+           		                     float num)
+           		                     throw(Error);
+static void		jsonNode_writeBool(ContainerNode*,
+           		                   const string &name,
+           		                   bool value)
+					   throw(Error);
+static void		jsonNode_writeString(ContainerNode*,
+           		                     const string &name,
+           		                     const string &value)
+					     throw(Error);
+static void		jsonNode_writeStringVector(ContainerNode*,
+           		                           const string &name,
+           		                           const StringVector &value)
+					           throw(Error);
+static ContainerNode 	jsonNode_writeNewContainer(ContainerNode*,
+                     	                           const string &name)
+					           throw(Error);
+static ContainerNode 	jsonNode_writeNewArray(ContainerNode*,
+                     	                       const string &name)
+					       throw(Error);
+
+static container_node_op json_op =
+{
+    &jsonNode_hasUnread,
+    &jsonNode_unreadName,
+    &jsonNode_readNumber,
+    &jsonNode_readBool,
+    &jsonNode_readString,
+    &jsonNode_readStringVector,
+    &jsonNode_readContainer,
+    &jsonNode_readArray,
+    &jsonNode_writeNumber,
+    &jsonNode_writeBool,
+    &jsonNode_writeString,
+    &jsonNode_writeStringVector,
+    &jsonNode_writeNewContainer,
+    &jsonNode_writeNewArray
+};
+
+///////////////////////////////////////////////////////////////////////////////
+JsonDocument::JsonDocument()
+: root(NULL)
+{
+    pj_caching_pool_init(&cp, NULL, 0);
+    pool = pj_pool_create(&cp.factory, "jsondoc", 512, 512, NULL);
+    if (!pool)
+	PJSUA2_RAISE_ERROR(PJ_ENOMEM);
+}
+
+JsonDocument::~JsonDocument()
+{
+    if (pool)
+	pj_pool_release(pool);
+    pj_caching_pool_destroy(&cp);
+}
+
+void JsonDocument::initRoot() const
+{
+    rootNode.op = &json_op;
+    rootNode.data.doc = (void*)this;
+    rootNode.data.data1 = (void*)root;
+    rootNode.data.data2 = root->value.children.next;
+}
+
+void JsonDocument::loadFile(const string &filename) throw(Error)
+{
+    if (root)
+	PJSUA2_RAISE_ERROR3(PJ_EINVALIDOP, "JsonDocument.loadString()",
+	                    "Document already initialized");
+
+    if (!pj_file_exists(filename.c_str()))
+	PJSUA2_RAISE_ERROR(PJ_ENOTFOUND);
+
+    pj_ssize_t size = (pj_ssize_t)pj_file_size(filename.c_str());
+    pj_status_t status;
+
+    char *buffer = (char*)pj_pool_alloc(pool, size+1);
+    pj_oshandle_t fd = 0;
+    unsigned parse_size;
+    char err_msg[120];
+    pj_json_err_info err_info;
+
+    err_msg[0] = '\0';
+
+    status = pj_file_open(pool, filename.c_str(), PJ_O_RDONLY, &fd);
+    if (status != PJ_SUCCESS)
+	goto on_error;
+
+    status = pj_file_read(fd, buffer, &size);
+    if (status != PJ_SUCCESS)
+	goto on_error;
+
+    pj_file_close(fd);
+    fd = NULL;
+
+    if (size <= 0) {
+	status = PJ_EEOF;
+	goto on_error;
+    }
+
+    parse_size = (unsigned)size;
+    root = pj_json_parse(pool, buffer, &parse_size, &err_info);
+    if (root == NULL) {
+	pj_ansi_snprintf(err_msg, sizeof(err_msg),
+	                 "JSON parsing failed: syntax error in file '%s' at "
+	                 "line %d column %d",
+	                 filename.c_str(), err_info.line, err_info.col);
+	PJ_LOG(1,(THIS_FILE, err_msg));
+	status = PJLIB_UTIL_EINJSON;
+	goto on_error;
+    }
+
+    initRoot();
+    return;
+
+on_error:
+    if (fd)
+	pj_file_close(fd);
+    if (err_msg[0])
+	PJSUA2_RAISE_ERROR3(status, "loadFile()", err_msg);
+    else
+	PJSUA2_RAISE_ERROR(status);
+}
+
+void JsonDocument::loadString(const string &input) throw(Error)
+{
+    if (root)
+	PJSUA2_RAISE_ERROR3(PJ_EINVALIDOP, "JsonDocument.loadString()",
+	                    "Document already initialized");
+
+    unsigned size = input.size();
+    char *buffer = (char*)pj_pool_alloc(pool, size+1);
+    unsigned parse_size = (unsigned)size;
+    pj_json_err_info err_info;
+
+    pj_memcpy(buffer, input.c_str(), size);
+
+    root = pj_json_parse(pool, buffer, &parse_size, &err_info);
+    if (root == NULL) {
+	char err_msg[80];
+
+	pj_ansi_snprintf(err_msg, sizeof(err_msg),
+	                 "JSON parsing failed at line %d column %d",
+	                 err_info.line, err_info.col);
+	PJ_LOG(1,(THIS_FILE, err_msg));
+	PJSUA2_RAISE_ERROR3(PJLIB_UTIL_EINJSON, "loadString()", err_msg);
+    }
+    initRoot();
+}
+
+struct save_file_data
+{
+    pj_oshandle_t fd;
+};
+
+static pj_status_t json_file_writer(const char *s,
+				    unsigned size,
+				    void *user_data)
+{
+    save_file_data *sd = (save_file_data*)user_data;
+    pj_ssize_t ssize = (pj_ssize_t)size;
+    return pj_file_write(sd->fd, s, &ssize);
+}
+
+void JsonDocument::saveFile(const string &filename) throw(Error)
+{
+    struct save_file_data sd;
+    pj_status_t status;
+
+    /* Make sure root container has been created */
+    getRootContainer();
+
+    status = pj_file_open(pool, filename.c_str(), PJ_O_WRONLY, &sd.fd);
+    if (status != PJ_SUCCESS)
+	PJSUA2_RAISE_ERROR(status);
+
+    status = pj_json_writef(root, &json_file_writer, &sd.fd);
+    pj_file_close(sd.fd);
+
+    if (status != PJ_SUCCESS)
+	PJSUA2_RAISE_ERROR(status);
+}
+
+struct save_string_data
+{
+    string	output;
+};
+
+static pj_status_t json_string_writer(const char *s,
+                                      unsigned size,
+                                      void *user_data)
+{
+    save_string_data *sd = (save_string_data*)user_data;
+    sd->output.append(s, size);
+    return PJ_SUCCESS;
+}
+
+string JsonDocument::saveString() throw(Error)
+{
+    struct save_string_data sd;
+    pj_status_t status;
+
+    /* Make sure root container has been created */
+    getRootContainer();
+
+    status = pj_json_writef(root, &json_string_writer, &sd);
+    if (status != PJ_SUCCESS)
+	PJSUA2_RAISE_ERROR(status);
+
+    return sd.output;
+}
+
+ContainerNode & JsonDocument::getRootContainer() const
+{
+    if (!root) {
+	root = allocElement();
+	pj_json_elem_obj(root, NULL);
+	initRoot();
+    }
+
+    return rootNode;
+}
+
+pj_json_elem* JsonDocument::allocElement() const
+{
+    return (pj_json_elem*)pj_pool_alloc(pool, sizeof(pj_json_elem));
+}
+
+pj_pool_t *JsonDocument::getPool()
+{
+    return pool;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+struct json_node_data
+{
+    JsonDocument	*doc;
+    pj_json_elem	*jnode;
+    pj_json_elem	*childPtr;
+};
+
+static bool jsonNode_hasUnread(const ContainerNode *node)
+{
+    json_node_data *jdat = (json_node_data*)&node->data;
+    return jdat->childPtr != (pj_json_elem*)&jdat->jnode->value.children;
+}
+
+static void json_verify(struct json_node_data *jdat,
+                        const char *op,
+                        const string &name,
+                        pj_json_val_type type)
+{
+    if (jdat->childPtr == (pj_json_elem*)&jdat->jnode->value.children)
+	PJSUA2_RAISE_ERROR3(PJ_EEOF, op, "No unread element");
+
+    /* If name is specified, then check if the names match, except
+     * when the node name itself is empty and the parent node is
+     * an array, then ignore the checking (JSON doesn't allow array
+     * elements to have name).
+     */
+    if (name.size() && name.compare(0, name.size(),
+                                    jdat->childPtr->name.ptr,
+                                    jdat->childPtr->name.slen) &&
+        jdat->childPtr->name.slen &&
+        jdat->jnode->type != PJ_JSON_VAL_ARRAY)
+    {
+	char err_msg[80];
+	pj_ansi_snprintf(err_msg, sizeof(err_msg),
+	                 "Name mismatch: expecting '%s' got '%.*s'",
+	                 name.c_str(), (int)jdat->childPtr->name.slen,
+	                 jdat->childPtr->name.ptr);
+	PJSUA2_RAISE_ERROR3(PJLIB_UTIL_EINJSON, op, err_msg);
+    }
+
+    if (type != PJ_JSON_VAL_NULL && jdat->childPtr->type != type) {
+	char err_msg[80];
+	pj_ansi_snprintf(err_msg, sizeof(err_msg),
+	                 "Type mismatch: expecting %d got %d",
+	                 type, jdat->childPtr->type);
+	PJSUA2_RAISE_ERROR3(PJLIB_UTIL_EINJSON, op, err_msg);
+    }
+}
+
+static string jsonNode_unreadName(const ContainerNode *node)
+				  throw(Error)
+{
+    json_node_data *jdat = (json_node_data*)&node->data;
+    json_verify(jdat, "unreadName()", "", PJ_JSON_VAL_NULL);
+    return pj2Str(jdat->childPtr->name);
+}
+
+static float jsonNode_readNumber(const ContainerNode *node,
+            		         const string &name)
+				 throw(Error)
+{
+    json_node_data *jdat = (json_node_data*)&node->data;
+    json_verify(jdat, "readNumber()", name, PJ_JSON_VAL_NUMBER);
+    jdat->childPtr = jdat->childPtr->next;
+    return jdat->childPtr->prev->value.num;
+}
+
+static bool jsonNode_readBool(const ContainerNode *node,
+			      const string &name)
+			      throw(Error)
+{
+    json_node_data *jdat = (json_node_data*)&node->data;
+    json_verify(jdat, "readBool()", name, PJ_JSON_VAL_BOOL);
+    jdat->childPtr = jdat->childPtr->next;
+    return PJ2BOOL(jdat->childPtr->prev->value.is_true);
+}
+
+static string jsonNode_readString(const ContainerNode *node,
+				  const string &name)
+				  throw(Error)
+{
+    json_node_data *jdat = (json_node_data*)&node->data;
+    json_verify(jdat, "readString()", name, PJ_JSON_VAL_STRING);
+    jdat->childPtr = jdat->childPtr->next;
+    return pj2Str(jdat->childPtr->prev->value.str);
+}
+
+static StringVector jsonNode_readStringVector(const ContainerNode *node,
+                   	                      const string &name)
+					      throw(Error)
+{
+    json_node_data *jdat = (json_node_data*)&node->data;
+    json_verify(jdat, "readStringVector()", name, PJ_JSON_VAL_ARRAY);
+
+    StringVector result;
+    pj_json_elem *child = jdat->childPtr->value.children.next;
+    while (child != (pj_json_elem*)&jdat->childPtr->value.children) {
+	if (child->type != PJ_JSON_VAL_STRING) {
+	    char err_msg[80];
+	    pj_ansi_snprintf(err_msg, sizeof(err_msg),
+			     "Elements not string but type %d",
+			     child->type);
+	    PJSUA2_RAISE_ERROR3(PJLIB_UTIL_EINJSON, "readStringVector()",
+	                        err_msg);
+	}
+	result.push_back(pj2Str(child->value.str));
+	child = child->next;
+    }
+
+    jdat->childPtr = jdat->childPtr->next;
+    return result;
+}
+
+static ContainerNode jsonNode_readContainer(const ContainerNode *node,
+                    	                    const string &name)
+					    throw(Error)
+{
+    json_node_data *jdat = (json_node_data*)&node->data;
+    json_verify(jdat, "readContainer()", name, PJ_JSON_VAL_OBJ);
+
+    ContainerNode json_node;
+
+    json_node.op = &json_op;
+    json_node.data.doc = (void*)jdat->doc;
+    json_node.data.data1 = (void*)jdat->childPtr;
+    json_node.data.data2 = (void*)jdat->childPtr->value.children.next;
+
+    jdat->childPtr = jdat->childPtr->next;
+    return json_node;
+}
+
+static ContainerNode jsonNode_readArray(const ContainerNode *node,
+                    	                const string &name)
+					throw(Error)
+{
+    json_node_data *jdat = (json_node_data*)&node->data;
+    json_verify(jdat, "readArray()", name, PJ_JSON_VAL_ARRAY);
+
+    ContainerNode json_node;
+
+    json_node.op = &json_op;
+    json_node.data.doc = (void*)jdat->doc;
+    json_node.data.data1 = (void*)jdat->childPtr;
+    json_node.data.data2 = (void*)jdat->childPtr->value.children.next;
+
+    jdat->childPtr = jdat->childPtr->next;
+    return json_node;
+}
+
+static pj_str_t alloc_name(JsonDocument *doc, const string &name)
+{
+    pj_str_t new_name;
+    pj_strdup2(doc->getPool(), &new_name, name.c_str());
+    return new_name;
+}
+
+static void jsonNode_writeNumber(ContainerNode *node,
+           		         const string &name,
+           		         float num)
+           		         throw(Error)
+{
+    json_node_data *jdat = (json_node_data*)&node->data;
+    pj_json_elem *el = jdat->doc->allocElement();
+    pj_str_t nm = alloc_name(jdat->doc, name);
+    pj_json_elem_number(el, &nm, num);
+    pj_json_elem_add(jdat->jnode, el);
+}
+
+static void jsonNode_writeBool(ContainerNode *node,
+           		       const string &name,
+           		       bool value)
+			       throw(Error)
+{
+    json_node_data *jdat = (json_node_data*)&node->data;
+    pj_json_elem *el = jdat->doc->allocElement();
+    pj_str_t nm = alloc_name(jdat->doc, name);
+    pj_json_elem_bool(el, &nm, value);
+    pj_json_elem_add(jdat->jnode, el);
+}
+
+static void jsonNode_writeString(ContainerNode *node,
+           		         const string &name,
+           		         const string &value)
+				 throw(Error)
+{
+    json_node_data *jdat = (json_node_data*)&node->data;
+    pj_json_elem *el = jdat->doc->allocElement();
+    pj_str_t nm = alloc_name(jdat->doc, name);
+    pj_str_t new_val;
+    pj_strdup2(jdat->doc->getPool(), &new_val, value.c_str());
+    pj_json_elem_string(el, &nm, &new_val);
+
+    pj_json_elem_add(jdat->jnode, el);
+}
+
+static void jsonNode_writeStringVector(ContainerNode *node,
+           		               const string &name,
+           		               const StringVector &value)
+				       throw(Error)
+{
+    json_node_data *jdat = (json_node_data*)&node->data;
+    pj_json_elem *el = jdat->doc->allocElement();
+    pj_str_t nm = alloc_name(jdat->doc, name);
+
+    pj_json_elem_array(el, &nm);
+    for (unsigned i=0; i<value.size(); ++i) {
+	pj_str_t new_val;
+
+	pj_strdup2(jdat->doc->getPool(), &new_val, value[i].c_str());
+	pj_json_elem *child = jdat->doc->allocElement();
+	pj_json_elem_string(child, NULL, &new_val);
+	pj_json_elem_add(el, child);
+    }
+
+    pj_json_elem_add(jdat->jnode, el);
+}
+
+static ContainerNode jsonNode_writeNewContainer(ContainerNode *node,
+                     	                        const string &name)
+					        throw(Error)
+{
+    json_node_data *jdat = (json_node_data*)&node->data;
+    pj_json_elem *el = jdat->doc->allocElement();
+    pj_str_t nm = alloc_name(jdat->doc, name);
+
+    pj_json_elem_obj(el, &nm);
+    pj_json_elem_add(jdat->jnode, el);
+
+    ContainerNode json_node;
+
+    json_node.op = &json_op;
+    json_node.data.doc = (void*)jdat->doc;
+    json_node.data.data1 = (void*)el;
+    json_node.data.data2 = (void*)el->value.children.next;
+
+    return json_node;
+}
+
+static ContainerNode jsonNode_writeNewArray(ContainerNode *node,
+                     	                    const string &name)
+					    throw(Error)
+{
+    json_node_data *jdat = (json_node_data*)&node->data;
+    pj_json_elem *el = jdat->doc->allocElement();
+    pj_str_t nm = alloc_name(jdat->doc, name);
+
+    pj_json_elem_array(el, &nm);
+    pj_json_elem_add(jdat->jnode, el);
+
+    ContainerNode json_node;
+
+    json_node.op = &json_op;
+    json_node.data.doc = (void*)jdat->doc;
+    json_node.data.data1 = (void*)el;
+    json_node.data.data2 = (void*)el->value.children.next;
+
+    return json_node;
+}
Index: /pjproject/trunk/pjsip/src/pjsua2/media.cpp
===================================================================
--- /pjproject/trunk/pjsip/src/pjsua2/media.cpp	(revision 4704)
+++ /pjproject/trunk/pjsip/src/pjsua2/media.cpp	(revision 4704)
@@ -0,0 +1,779 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2013 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 <pj/ctype.h>
+#include <pjsua2/media.hpp>
+#include <pjsua2/types.hpp>
+#include <pjsua2/endpoint.hpp>
+#include "util.hpp"
+
+using namespace pj;
+using namespace std;
+
+#define THIS_FILE		"media.cpp"
+#define MAX_FILE_NAMES 		64
+#define MAX_DEV_COUNT		64
+
+///////////////////////////////////////////////////////////////////////////////
+void MediaFormatAudio::fromPj(const pjmedia_format &format)
+{
+    if ((format.type != PJMEDIA_TYPE_AUDIO) &&
+	(format.detail_type != PJMEDIA_FORMAT_DETAIL_AUDIO))
+    {
+	type = PJMEDIA_TYPE_UNKNOWN;
+	return;
+    }
+
+    id = format.id;
+    type = format.type;
+
+    /* Detail. */
+    clockRate = format.det.aud.clock_rate;
+    channelCount = format.det.aud.channel_count;
+    frameTimeUsec = format.det.aud.frame_time_usec;
+    bitsPerSample = format.det.aud.bits_per_sample;
+    avgBps = format.det.aud.avg_bps;
+    maxBps = format.det.aud.max_bps;
+}
+
+pjmedia_format MediaFormatAudio::toPj() const
+{
+    pjmedia_format pj_format;
+
+    pj_format.id = id;
+    pj_format.type = type;
+
+    pj_format.detail_type = PJMEDIA_FORMAT_DETAIL_AUDIO;
+    pj_format.det.aud.clock_rate = clockRate;
+    pj_format.det.aud.channel_count = channelCount;
+    pj_format.det.aud.frame_time_usec = frameTimeUsec;
+    pj_format.det.aud.bits_per_sample = bitsPerSample;
+    pj_format.det.aud.avg_bps = avgBps;
+    pj_format.det.aud.max_bps = maxBps;
+
+    return pj_format;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+/* Audio Media operations. */
+void ConfPortInfo::fromPj(const pjsua_conf_port_info &port_info)
+{
+    portId = port_info.slot_id;
+    name = pj2Str(port_info.name);
+    format.fromPj(port_info.format);
+    txLevelAdj = port_info.tx_level_adj;
+    rxLevelAdj = port_info.rx_level_adj;
+
+    /*
+    format.id = PJMEDIA_FORMAT_PCM;
+    format.type = PJMEDIA_TYPE_AUDIO;
+    format.clockRate = port_info.clock_rate;
+    format.channelCount = port_info.channel_count;
+    format.bitsPerSample = port_info.bits_per_sample;
+    format.frameTimeUsec = (port_info.samples_per_frame *
+			   1000000) /
+			   (port_info.clock_rate *
+			   port_info.channel_count);
+
+    format.avgBps = format.maxBps = port_info.clock_rate *
+				    port_info.channel_count *
+				    port_info.bits_per_sample;
+    */
+    listeners.clear();
+    for (unsigned i=0; i<port_info.listener_cnt; ++i) {
+	listeners.push_back(port_info.listeners[i]);
+    }
+}
+///////////////////////////////////////////////////////////////////////////////
+Media::Media(pjmedia_type med_type)
+: type(med_type)
+{
+
+}
+
+Media::~Media()
+{
+
+}
+
+pjmedia_type Media::getType() const
+{
+    return type;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+AudioMedia::AudioMedia() 
+: Media(PJMEDIA_TYPE_AUDIO), id(PJSUA_INVALID_ID), mediaPool(NULL)
+{
+
+}
+
+void AudioMedia::registerMediaPort(MediaPort port) throw(Error)
+{
+    /* Check if media already added to Conf bridge. */
+    pj_assert(!Endpoint::instance().mediaExists(*this));
+
+    if (port != NULL) {
+	pj_assert(id == PJSUA_INVALID_ID);
+
+	pj_caching_pool_init(&mediaCachingPool, NULL, 0);
+
+	mediaPool = pj_pool_create(&mediaCachingPool.factory,
+				   "media",
+				   512,
+				   512,
+				   NULL);
+
+	if (!mediaPool) {
+	    pj_caching_pool_destroy(&mediaCachingPool);
+	    PJSUA2_RAISE_ERROR(PJ_ENOMEM);
+	}
+
+	PJSUA2_CHECK_EXPR( pjsua_conf_add_port(mediaPool,
+					       (pjmedia_port *)port,
+					       &id) );
+    }
+
+    Endpoint::instance().mediaAdd(*this);
+}
+
+void AudioMedia::unregisterMediaPort()
+{
+    if (id != PJSUA_INVALID_ID) {
+	pjsua_conf_remove_port(id);
+	id = PJSUA_INVALID_ID;
+    }
+
+    if (mediaPool) {
+	pj_pool_release(mediaPool);
+	mediaPool = NULL;
+	pj_caching_pool_destroy(&mediaCachingPool);
+    }
+
+    Endpoint::instance().mediaRemove(*this);
+}
+
+AudioMedia::~AudioMedia() 
+{
+    unregisterMediaPort();
+}
+
+ConfPortInfo AudioMedia::getPortInfo() const throw(Error)
+{
+    return AudioMedia::getPortInfoFromId(id);
+}
+
+int AudioMedia::getPortId() const
+{
+    return id;
+}
+
+ConfPortInfo AudioMedia::getPortInfoFromId(int port_id) throw(Error)
+{
+    pjsua_conf_port_info pj_info;
+    ConfPortInfo pi;
+
+    PJSUA2_CHECK_EXPR( pjsua_conf_get_port_info(port_id, &pj_info) );
+    pi.fromPj(pj_info);
+    return pi;
+}
+
+void AudioMedia::startTransmit(const AudioMedia &sink) const throw(Error)
+{
+    PJSUA2_CHECK_EXPR( pjsua_conf_connect(id, sink.id) );
+}
+
+void AudioMedia::stopTransmit(const AudioMedia &sink) const throw(Error)
+{
+    PJSUA2_CHECK_EXPR( pjsua_conf_disconnect(id, sink.id) );
+}
+
+void AudioMedia::adjustRxLevel(float level) throw(Error)
+{
+    PJSUA2_CHECK_EXPR( pjsua_conf_adjust_rx_level(id, level) );
+}
+
+void AudioMedia::adjustTxLevel(float level) throw(Error)
+{
+    PJSUA2_CHECK_EXPR( pjsua_conf_adjust_tx_level(id, level) );
+}
+
+unsigned AudioMedia::getRxLevel() const throw(Error)
+{
+    return getSignalLevel(true);
+}
+
+unsigned AudioMedia::getTxLevel() const throw(Error)
+{
+    return getSignalLevel(false);
+}
+
+unsigned AudioMedia::getSignalLevel(bool is_rx) const throw(Error)
+{    
+    unsigned rx_level;
+    unsigned tx_level;
+    
+    PJSUA2_CHECK_EXPR( pjsua_conf_get_signal_level(id, &tx_level, &rx_level) );
+    return is_rx?rx_level:tx_level;
+}
+
+AudioMedia* AudioMedia::typecastFromMedia(Media *media)
+{
+    return static_cast<AudioMedia*>(media);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+AudioMediaPlayer::AudioMediaPlayer()
+: playerId(PJSUA_INVALID_ID)
+{
+
+}
+
+AudioMediaPlayer::~AudioMediaPlayer()
+{
+    if (playerId != PJSUA_INVALID_ID)
+	pjsua_player_destroy(playerId);
+}
+
+void AudioMediaPlayer::createPlayer(const string &file_name,
+				    unsigned options)
+				    throw(Error)
+{
+    pj_str_t pj_name = str2Pj(file_name);
+
+    PJSUA2_CHECK_EXPR( pjsua_player_create(&pj_name,
+					   options, 
+					   &playerId) );
+
+    /* Get media port id. */
+    id = pjsua_player_get_conf_port(playerId);
+
+    registerMediaPort(NULL);
+}
+
+void AudioMediaPlayer::createPlaylist(const StringVector &file_names,
+				      const string &label,
+				      unsigned options)
+				      throw(Error)
+{
+    pj_str_t pj_files[MAX_FILE_NAMES];
+    unsigned i, count = 0;
+    pj_str_t pj_lbl = str2Pj(label);
+
+    count = PJ_ARRAY_SIZE(pj_files);
+
+    for(i=0; i<file_names.size() && i<count;++i)
+    {
+	const string &file_name = file_names[i];
+	
+	pj_files[i] = str2Pj(file_name);
+    }
+
+    PJSUA2_CHECK_EXPR( pjsua_playlist_create(pj_files,
+					     i,
+					     &pj_lbl,
+					     options, 
+					     &playerId) );
+
+    /* Get media port id. */
+    id = pjsua_player_get_conf_port(playerId);
+
+    registerMediaPort(NULL);
+}
+
+void AudioMediaPlayer::setPos(pj_uint32_t samples) throw(Error)
+{
+    PJSUA2_CHECK_EXPR( pjsua_player_set_pos(playerId, samples) );
+}
+
+AudioMediaPlayer* AudioMediaPlayer::typecastFromAudioMedia(
+						AudioMedia *media)
+{
+    return static_cast<AudioMediaPlayer*>(media);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+AudioMediaRecorder::AudioMediaRecorder()
+: recorderId(PJSUA_INVALID_ID)
+{
+
+}
+
+AudioMediaRecorder::~AudioMediaRecorder()
+{
+    if (recorderId != PJSUA_INVALID_ID)
+	pjsua_recorder_destroy(recorderId);
+}
+
+void AudioMediaRecorder::createRecorder(const string &file_name,
+				        unsigned enc_type,
+				        pj_ssize_t max_size,
+				        unsigned options)
+				        throw(Error)
+{
+    PJ_UNUSED_ARG(max_size);
+
+    pj_str_t pj_name = str2Pj(file_name);
+
+    PJSUA2_CHECK_EXPR( pjsua_recorder_create(&pj_name,
+					     enc_type,
+					     NULL,
+					     -1,
+					     options,
+					     &recorderId) );
+
+    /* Get media port id. */
+    id = pjsua_recorder_get_conf_port(recorderId);
+
+    registerMediaPort(NULL);
+}
+
+AudioMediaRecorder* AudioMediaRecorder::typecastFromAudioMedia(
+						AudioMedia *media)
+{
+    return static_cast<AudioMediaRecorder*>(media);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+void AudioDevInfo::fromPj(const pjmedia_aud_dev_info &dev_info)
+{
+    name = dev_info.name;
+    inputCount = dev_info.input_count;
+    outputCount = dev_info.output_count;
+    defaultSamplesPerSec = dev_info.default_samples_per_sec;
+    driver = dev_info.driver;
+    caps = dev_info.caps;
+    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)
+	    extFmt.push_back(format);
+    }
+}
+
+AudioDevInfo::~AudioDevInfo()
+{
+    for(unsigned i=0;i<extFmt.size();++i) {
+	delete extFmt[i];
+    }
+    extFmt.clear();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * Simple AudioMedia class for sound device.
+ */
+class DevAudioMedia : public AudioMedia
+{
+public:
+    DevAudioMedia();
+    ~DevAudioMedia();
+};
+
+DevAudioMedia::DevAudioMedia()
+{
+    this->id = 0;
+    registerMediaPort(NULL);
+}
+
+DevAudioMedia::~DevAudioMedia()
+{
+    /* Avoid removing this port (conf port id=0) from conference */
+    this->id = PJSUA_INVALID_ID;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+/* Audio device operations. */
+
+AudDevManager::AudDevManager()
+: devMedia(NULL)
+{
+}
+
+AudDevManager::~AudDevManager()
+{
+    // At this point, devMedia should have been cleaned up by Endpoint,
+    // as AudDevManager destructor is called after Endpoint destructor.
+    //delete devMedia;
+    
+    clearAudioDevList();
+}
+
+int AudDevManager::getCaptureDev() const throw(Error)
+{
+    return getActiveDev(true);
+}
+
+AudioMedia &AudDevManager::getCaptureDevMedia() throw(Error)
+{
+    if (!devMedia)
+	devMedia = new DevAudioMedia;
+    return *devMedia;
+}
+
+int AudDevManager::getPlaybackDev() const throw(Error)
+{
+    return getActiveDev(false);
+}
+
+AudioMedia &AudDevManager::getPlaybackDevMedia() throw(Error)
+{
+    if (!devMedia)
+    	devMedia = new DevAudioMedia;
+    return *devMedia;
+}
+
+void AudDevManager::setCaptureDev(int capture_dev) const throw(Error)
+{
+    int playback_dev = getPlaybackDev();
+
+    PJSUA2_CHECK_EXPR( pjsua_set_snd_dev(capture_dev, playback_dev) );
+}
+
+void AudDevManager::setPlaybackDev(int playback_dev) const throw(Error)
+{
+    int capture_dev = getCaptureDev();
+
+    PJSUA2_CHECK_EXPR( pjsua_set_snd_dev(capture_dev, playback_dev) );
+}
+
+const AudioDevInfoVector &AudDevManager::enumDev() throw(Error)
+{
+    pjmedia_aud_dev_info pj_info[MAX_DEV_COUNT];
+    unsigned count;
+
+    PJSUA2_CHECK_EXPR( pjsua_enum_aud_devs(pj_info, &count) );
+
+    pj_enter_critical_section();
+    clearAudioDevList();
+    for (unsigned i = 0; (i<count && i<MAX_DEV_COUNT) ;++i) {
+	AudioDevInfo *dev_info = new AudioDevInfo;
+	dev_info->fromPj(pj_info[i]);
+	audioDevList.push_back(dev_info);
+    }
+    pj_leave_critical_section();
+    return audioDevList;
+}
+
+void AudDevManager::setNullDev() throw(Error)
+{
+    PJSUA2_CHECK_EXPR( pjsua_set_null_snd_dev() );
+}
+
+MediaPort *AudDevManager::setNoDev()
+{
+    return (MediaPort*)pjsua_set_no_snd_dev();
+}
+
+void AudDevManager::setEcOptions(unsigned tail_msec,
+				 unsigned options) throw(Error)
+{
+    PJSUA2_CHECK_EXPR( pjsua_set_ec(tail_msec, options) );
+}
+
+unsigned AudDevManager::getEcTail() const throw(Error)
+{
+    unsigned tail_msec = 0;
+
+    PJSUA2_CHECK_EXPR( pjsua_get_ec_tail(&tail_msec) );
+
+    return tail_msec;
+}
+
+bool AudDevManager::sndIsActive() const
+{
+    return PJ2BOOL(pjsua_snd_is_active());
+}
+
+void AudDevManager::refreshDevs() throw(Error)
+{
+    PJSUA2_CHECK_EXPR( pjmedia_aud_dev_refresh() );
+}
+
+unsigned AudDevManager::getDevCount() const
+{
+    return pjmedia_aud_dev_count();
+}
+
+AudioDevInfo
+AudDevManager::getDevInfo(int id) const throw(Error)
+{
+    AudioDevInfo dev_info;
+    pjmedia_aud_dev_info pj_info;
+
+    PJSUA2_CHECK_EXPR( pjmedia_aud_dev_get_info(id, &pj_info) );
+
+    dev_info.fromPj(pj_info);
+    return dev_info;
+}
+
+int AudDevManager::lookupDev(const string &drv_name,
+			     const string &dev_name) const throw(Error)
+{
+    pjmedia_aud_dev_index pj_idx = 0;
+
+    PJSUA2_CHECK_EXPR( pjmedia_aud_dev_lookup(drv_name.c_str(),
+					      dev_name.c_str(),
+					      &pj_idx) );
+
+    return pj_idx;
+}
+
+
+string AudDevManager::capName(pjmedia_aud_dev_cap cap) const
+{
+    return pjmedia_aud_dev_cap_name(cap, NULL);
+}
+
+void
+AudDevManager::setExtFormat(const MediaFormatAudio &format,
+			    bool keep) throw(Error)
+{
+    pjmedia_format pj_format = format.toPj();
+
+    PJSUA2_CHECK_EXPR( pjsua_snd_set_setting(PJMEDIA_AUD_DEV_CAP_EXT_FORMAT,
+					     &pj_format,
+					     keep) );
+}
+
+MediaFormatAudio AudDevManager::getExtFormat() const throw(Error)
+{
+    pjmedia_format pj_format;
+    MediaFormatAudio format;
+
+    PJSUA2_CHECK_EXPR( pjsua_snd_get_setting(PJMEDIA_AUD_DEV_CAP_EXT_FORMAT,
+					     &pj_format) );
+
+    format.fromPj(pj_format);
+
+    return format;
+}
+
+void AudDevManager::setInputLatency(unsigned latency_msec,
+				    bool keep) throw(Error)
+{
+    PJSUA2_CHECK_EXPR( pjsua_snd_set_setting(PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY,
+					     &latency_msec,
+					     keep) );
+}
+
+unsigned AudDevManager::getInputLatency() const throw(Error)
+{
+    unsigned latency_msec = 0;
+
+    PJSUA2_CHECK_EXPR( pjsua_snd_get_setting(PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY,
+					     &latency_msec) );
+
+    return latency_msec;
+}
+
+void
+AudDevManager::setOutputLatency(unsigned latency_msec,
+				bool keep) throw(Error)
+{
+    PJSUA2_CHECK_EXPR( pjsua_snd_set_setting(PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY,
+					     &latency_msec,
+					     keep) );
+}
+
+unsigned AudDevManager::getOutputLatency() const throw(Error)
+{
+    unsigned latency_msec = 0;
+
+    PJSUA2_CHECK_EXPR( pjsua_snd_get_setting(PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY,
+					     &latency_msec) );
+
+    return latency_msec;
+}
+
+void AudDevManager::setInputVolume(unsigned volume, bool keep) throw(Error)
+{
+    PJSUA2_CHECK_EXPR(
+	    pjsua_snd_set_setting(PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING,
+				  &volume,
+				  keep) );
+}
+
+unsigned AudDevManager::getInputVolume() const throw(Error)
+{
+    unsigned volume = 0;
+
+    PJSUA2_CHECK_EXPR(
+	    pjsua_snd_get_setting(PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING,
+				  &volume) );
+
+    return volume;
+}
+
+void AudDevManager::setOutputVolume(unsigned volume, bool keep) throw(Error)
+{
+    PJSUA2_CHECK_EXPR(
+	    pjsua_snd_set_setting(PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING,
+				  &volume,
+				  keep) );
+}
+
+unsigned AudDevManager::getOutputVolume() const throw(Error)
+{
+    unsigned volume = 0;
+
+    PJSUA2_CHECK_EXPR(
+	    pjsua_snd_get_setting(PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING,
+				  &volume) );
+
+    return volume;
+}
+
+unsigned AudDevManager::getInputSignal() const throw(Error)
+{
+    unsigned signal = 0;
+
+    PJSUA2_CHECK_EXPR(
+	    pjsua_snd_get_setting(PJMEDIA_AUD_DEV_CAP_INPUT_SIGNAL_METER,
+				  &signal) );
+
+    return signal;
+}
+
+unsigned AudDevManager::getOutputSignal() const throw(Error)
+{
+    unsigned signal = 0;
+
+    PJSUA2_CHECK_EXPR(
+	    pjsua_snd_get_setting(PJMEDIA_AUD_DEV_CAP_OUTPUT_SIGNAL_METER,
+				  &signal) );
+
+    return signal;
+}
+
+void
+AudDevManager::setInputRoute(pjmedia_aud_dev_route route,
+			     bool keep) throw(Error)
+{
+    PJSUA2_CHECK_EXPR( pjsua_snd_set_setting(PJMEDIA_AUD_DEV_CAP_INPUT_ROUTE,
+					     &route,
+					     keep) );
+}
+
+pjmedia_aud_dev_route AudDevManager::getInputRoute() const throw(Error)
+{
+    pjmedia_aud_dev_route route = PJMEDIA_AUD_DEV_ROUTE_DEFAULT;
+
+    PJSUA2_CHECK_EXPR( pjsua_snd_get_setting(PJMEDIA_AUD_DEV_CAP_INPUT_ROUTE,
+					     &route) );
+
+    return route;
+}
+
+void
+AudDevManager::setOutputRoute(pjmedia_aud_dev_route route,
+			      bool keep) throw(Error)
+{
+    PJSUA2_CHECK_EXPR( pjsua_snd_set_setting(PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE,
+					     &route,
+					     keep) );
+}
+
+pjmedia_aud_dev_route AudDevManager::getOutputRoute() const throw(Error)
+{
+    pjmedia_aud_dev_route route = PJMEDIA_AUD_DEV_ROUTE_DEFAULT;
+
+    PJSUA2_CHECK_EXPR( pjsua_snd_get_setting(PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE,
+					     &route) );
+
+    return route;
+}
+
+void AudDevManager::setVad(bool enable, bool keep) throw(Error)
+{
+    PJSUA2_CHECK_EXPR( pjsua_snd_set_setting(PJMEDIA_AUD_DEV_CAP_VAD,
+					     &enable,
+					     keep) );
+}
+
+bool AudDevManager::getVad() const throw(Error)
+{
+    bool enable = false;
+
+    PJSUA2_CHECK_EXPR( pjsua_snd_get_setting(PJMEDIA_AUD_DEV_CAP_VAD,
+					     &enable) );
+
+    return enable;
+}
+
+void AudDevManager::setCng(bool enable, bool keep) throw(Error)
+{
+    PJSUA2_CHECK_EXPR( pjsua_snd_set_setting(PJMEDIA_AUD_DEV_CAP_CNG,
+					     &enable,
+					     keep) );
+}
+
+bool AudDevManager::getCng() const throw(Error)
+{
+    bool enable = false;
+
+    PJSUA2_CHECK_EXPR( pjsua_snd_get_setting(PJMEDIA_AUD_DEV_CAP_CNG,
+					     &enable) );
+
+    return enable;
+}
+
+void AudDevManager::setPlc(bool enable, bool keep) throw(Error)
+{
+    PJSUA2_CHECK_EXPR( pjsua_snd_set_setting(PJMEDIA_AUD_DEV_CAP_PLC,
+					     &enable,
+					     keep) );
+}
+
+bool AudDevManager::getPlc() const throw(Error)
+{
+    bool enable = false;
+
+    PJSUA2_CHECK_EXPR( pjsua_snd_get_setting(PJMEDIA_AUD_DEV_CAP_PLC,
+					     &enable) );
+
+    return enable;
+}
+
+void AudDevManager::clearAudioDevList()
+{
+    for(unsigned i=0;i<audioDevList.size();++i) {
+	delete audioDevList[i];
+    }
+    audioDevList.clear();
+}
+
+int AudDevManager::getActiveDev(bool is_capture) const throw(Error)
+{
+    int capture_dev = 0, playback_dev = 0;
+    PJSUA2_CHECK_EXPR( pjsua_get_snd_dev(&capture_dev, &playback_dev) );
+
+    return is_capture?capture_dev:playback_dev;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+void CodecInfo::fromPj(const pjsua_codec_info &codec_info)
+{
+    codecId = pj2Str(codec_info.codec_id);
+    priority = codec_info.priority;
+    desc = pj2Str(codec_info.desc);
+}
Index: /pjproject/trunk/pjsip/src/pjsua2/persistent.cpp
===================================================================
--- /pjproject/trunk/pjsip/src/pjsua2/persistent.cpp	(revision 4704)
+++ /pjproject/trunk/pjsip/src/pjsua2/persistent.cpp	(revision 4704)
@@ -0,0 +1,227 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2013 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 <pjsua2/persistent.hpp>
+
+using namespace pj;
+using namespace std;
+
+
+bool PersistentDocument::hasUnread() const
+{
+    return getRootContainer().hasUnread();
+}
+
+string PersistentDocument::unreadName() const throw(Error)
+{
+    return getRootContainer().unreadName();
+}
+
+int PersistentDocument::readInt(const string &name) const throw(Error)
+{
+    return (int)getRootContainer().readNumber(name);
+}
+
+float PersistentDocument::readNumber(const string &name) const throw(Error)
+{
+    return getRootContainer().readNumber(name);
+}
+
+bool PersistentDocument::readBool(const string &name) const throw(Error)
+{
+    return getRootContainer().readBool(name);
+}
+
+string PersistentDocument::readString(const string &name) const throw(Error)
+{
+    return getRootContainer().readString(name);
+}
+
+StringVector PersistentDocument::readStringVector(const string &name) const
+						  throw(Error)
+{
+    return getRootContainer().readStringVector(name);
+}
+
+void PersistentDocument::readObject(PersistentObject &obj) const throw(Error)
+{
+    getRootContainer().readObject(obj);
+}
+
+ContainerNode PersistentDocument::readContainer(const string &name) const
+					        throw(Error)
+{
+    return getRootContainer().readContainer(name);
+}
+
+ContainerNode PersistentDocument::readArray(const string &name) const
+					    throw(Error)
+{
+    return getRootContainer().readArray(name);
+}
+
+void PersistentDocument::writeNumber(const string &name,
+				     float num) throw(Error)
+{
+    getRootContainer().writeNumber(name, num);
+}
+
+void PersistentDocument::writeInt(const string &name,
+				  int num) throw(Error)
+{
+    getRootContainer().writeNumber(name, (float)num);
+}
+
+void PersistentDocument::writeBool(const string &name,
+				   bool value) throw(Error)
+{
+    getRootContainer().writeBool(name, value);
+}
+
+void PersistentDocument::writeString(const string &name,
+                                     const string &value) throw(Error)
+{
+    getRootContainer().writeString(name, value);
+}
+
+void PersistentDocument::writeStringVector(const string &name,
+                                           const StringVector &value)
+					   throw(Error)
+{
+    getRootContainer().writeStringVector(name, value);
+}
+
+void PersistentDocument::writeObject(const PersistentObject &obj) throw(Error)
+{
+    getRootContainer().writeObject(obj);
+}
+
+ContainerNode PersistentDocument::writeNewContainer(const string &name)
+						    throw(Error)
+{
+    return getRootContainer().writeNewContainer(name);
+}
+
+ContainerNode PersistentDocument::writeNewArray(const string &name)
+						    throw(Error)
+{
+    return getRootContainer().writeNewArray(name);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool ContainerNode::hasUnread() const
+{
+    return op->hasUnread(this);
+}
+
+string ContainerNode::unreadName() const throw(Error)
+{
+    return op->unreadName(this);
+}
+
+int ContainerNode::readInt(const string &name) const throw(Error)
+{
+    return (int)op->readNumber(this, name);
+}
+
+float ContainerNode::readNumber(const string &name) const throw(Error)
+{
+    return op->readNumber(this, name);
+}
+
+bool ContainerNode::readBool(const string &name) const throw(Error)
+{
+    return op->readBool(this, name);
+}
+
+string ContainerNode::readString(const string &name) const throw(Error)
+{
+    return op->readString(this, name);
+}
+
+StringVector ContainerNode::readStringVector(const string &name) const
+					     throw(Error)
+{
+    return op->readStringVector(this, name);
+}
+
+void ContainerNode::readObject(PersistentObject &obj) const throw(Error)
+{
+    obj.readObject(*this);
+}
+
+ContainerNode ContainerNode::readContainer(const string &name) const
+					   throw(Error)
+{
+    return op->readContainer(this, name);
+}
+
+ContainerNode ContainerNode::readArray(const string &name) const
+					   throw(Error)
+{
+    return op->readArray(this, name);
+}
+
+void ContainerNode::writeNumber(const string &name,
+				float num) throw(Error)
+{
+    return op->writeNumber(this, name, num);
+}
+
+void ContainerNode::writeInt(const string &name,
+			     int num) throw(Error)
+{
+    return op->writeNumber(this, name, (float)num);
+}
+
+void ContainerNode::writeBool(const string &name,
+			      bool value) throw(Error)
+{
+    return op->writeBool(this, name, value);
+}
+
+void ContainerNode::writeString(const string &name,
+				const string &value) throw(Error)
+{
+    return op->writeString(this, name, value);
+}
+
+void ContainerNode::writeStringVector(const string &name,
+				      const StringVector &value)
+				      throw(Error)
+{
+    return op->writeStringVector(this, name, value);
+}
+
+void ContainerNode::writeObject(const PersistentObject &obj) throw(Error)
+{
+    obj.writeObject(*this);
+}
+
+ContainerNode ContainerNode::writeNewContainer(const string &name)
+					       throw(Error)
+{
+    return op->writeNewContainer(this, name);
+}
+
+ContainerNode ContainerNode::writeNewArray(const string &name)
+					   throw(Error)
+{
+    return op->writeNewArray(this, name);
+}
Index: /pjproject/trunk/pjsip/src/pjsua2/presence.cpp
===================================================================
--- /pjproject/trunk/pjsip/src/pjsua2/presence.cpp	(revision 4704)
+++ /pjproject/trunk/pjsip/src/pjsua2/presence.cpp	(revision 4704)
@@ -0,0 +1,190 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2013 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 <pjsua2/presence.hpp>
+#include <pjsua2/account.hpp>
+#include "util.hpp"
+
+using namespace pj;
+using namespace std;
+
+#define THIS_FILE		"presence.cpp"
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+PresenceStatus::PresenceStatus()
+: status(PJSUA_BUDDY_STATUS_UNKNOWN), activity(PJRPID_ACTIVITY_UNKNOWN)
+{
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+void BuddyConfig::readObject(const ContainerNode &node) throw(Error)
+{
+    ContainerNode this_node = node.readContainer("BuddyConfig");
+
+    NODE_READ_STRING   ( this_node, uri);
+    NODE_READ_BOOL     ( this_node, subscribe);
+}
+
+void BuddyConfig::writeObject(ContainerNode &node) const throw(Error)
+{
+    ContainerNode this_node = node.writeNewContainer("BuddyConfig");
+
+    NODE_WRITE_STRING  ( this_node, uri);
+    NODE_WRITE_BOOL    ( this_node, subscribe);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+void BuddyInfo::fromPj(const pjsua_buddy_info &pbi)
+{
+    uri 		= pj2Str(pbi.uri);
+    contact 		= pj2Str(pbi.contact);
+    presMonitorEnabled 	= PJ2BOOL(pbi.monitor_pres);
+    subState 		= pbi.sub_state;
+    subStateName 	= string(pbi.sub_state_name);
+    subTermCode 	= (pjsip_status_code)pbi.sub_term_code;
+    subTermReason 	= pj2Str(pbi.sub_term_reason);
+    
+    /* Presence status */
+    presStatus.status	= pbi.status;
+    presStatus.statusText = pj2Str(pbi.status_text);
+    presStatus.activity = pbi.rpid.activity;
+    presStatus.note	= pj2Str(pbi.rpid.note);
+    presStatus.rpidId	= pj2Str(pbi.rpid.id);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+/*
+ * Constructor.
+ */
+Buddy::Buddy()
+: id(PJSUA_INVALID_ID)
+{
+}
+ 
+/*
+ * Destructor.
+ */
+Buddy::~Buddy()
+{
+    if (isValid()) {
+	pjsua_buddy_set_user_data(id, NULL);
+	pjsua_buddy_del(id);
+
+	/* Remove from account buddy list */
+	acc->removeBuddy(this);
+    }
+}
+    
+/*
+ * Create buddy and register the buddy to PJSUA-LIB.
+ */
+void Buddy::create(Account &account, const BuddyConfig &cfg) throw(Error)
+{
+    pjsua_buddy_config pj_cfg;
+    pjsua_buddy_config_default(&pj_cfg);
+    
+    if (!account.isValid())
+	PJSUA2_RAISE_ERROR3(PJ_EINVAL, "Buddy::create()", "Invalid account");
+    
+    pj_cfg.uri = str2Pj(cfg.uri);
+    pj_cfg.subscribe = cfg.subscribe;
+    pj_cfg.user_data = (void*)this;
+    PJSUA2_CHECK_EXPR( pjsua_buddy_add(&pj_cfg, &id) );
+    
+    acc = &account;
+    acc->addBuddy(this);
+}
+    
+/*
+ * Check if this buddy is valid.
+ */
+bool Buddy::isValid() const
+{
+    return PJ2BOOL( pjsua_buddy_is_valid(id) );
+}
+
+/*
+ * Get detailed buddy info.
+ */
+BuddyInfo Buddy::getInfo() const throw(Error)
+{
+    pjsua_buddy_info pj_bi;
+    BuddyInfo bi;
+
+    PJSUA2_CHECK_EXPR( pjsua_buddy_get_info(id, &pj_bi) );
+    bi.fromPj(pj_bi);
+    return bi;
+}
+
+/*
+ * Enable/disable buddy's presence monitoring.
+ */
+void Buddy::subscribePresence(bool subscribe) throw(Error)
+{
+    PJSUA2_CHECK_EXPR( pjsua_buddy_subscribe_pres(id, subscribe) );
+}
+
+    
+/*
+ * Update the presence information for the buddy.
+ */
+void Buddy::updatePresence(void) throw(Error)
+{
+    PJSUA2_CHECK_EXPR( pjsua_buddy_update_pres(id) );
+}
+     
+/*
+ * Send instant messaging outside dialog.
+ */
+void Buddy::sendInstantMessage(const SendInstantMessageParam &prm) throw(Error)
+{
+    BuddyInfo bi = getInfo();
+
+    pj_str_t to = str2Pj(bi.contact.empty()? bi.uri : bi.contact);
+    pj_str_t mime_type = str2Pj(prm.contentType);
+    pj_str_t content = str2Pj(prm.content);
+    void *user_data = (void*)prm.userData;
+    pjsua_msg_data msg_data;
+    prm.txOption.toPj(msg_data);
+    
+    PJSUA2_CHECK_EXPR( pjsua_im_send(acc->getId(), &to, &mime_type, &content,
+				     &msg_data, user_data) );
+}
+
+/*
+ * Send typing indication outside dialog.
+ */
+void Buddy::sendTypingIndication(const SendTypingIndicationParam &prm)
+     throw(Error)
+{
+    BuddyInfo bi = getInfo();
+
+    pj_str_t to = str2Pj(bi.contact.empty()? bi.uri : bi.contact);
+    pjsua_msg_data msg_data;
+    prm.txOption.toPj(msg_data);
+    
+    PJSUA2_CHECK_EXPR( pjsua_im_typing(acc->getId(), &to, prm.isTyping,
+				       &msg_data) );
+}
+
Index: /pjproject/trunk/pjsip/src/pjsua2/siptypes.cpp
===================================================================
--- /pjproject/trunk/pjsip/src/pjsua2/siptypes.cpp	(revision 4704)
+++ /pjproject/trunk/pjsip/src/pjsua2/siptypes.cpp	(revision 4704)
@@ -0,0 +1,590 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2013 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 <pjsua2/types.hpp>
+#include <pjsua2/siptypes.hpp>
+#include "util.hpp"
+
+using namespace pj;
+using namespace std;
+
+#define THIS_FILE	"siptypes.cpp"
+
+///////////////////////////////////////////////////////////////////////////////
+namespace pj
+{
+void readIntVector( ContainerNode &node,
+                    const string &array_name,
+                    IntVector &v) throw(Error)
+{
+    ContainerNode array_node = node.readArray(array_name);
+    v.resize(0);
+    while (array_node.hasUnread()) {
+	v.push_back((int)array_node.readNumber());
+    }
+}
+
+void writeIntVector(ContainerNode &node,
+                    const string &array_name,
+                    const IntVector &v) throw(Error)
+{
+    ContainerNode array_node = node.writeNewArray(array_name);
+    for (unsigned i=0; i<v.size(); ++i) {
+	array_node.writeNumber("", (float)v[i]);
+    }
+}
+
+void readQosParams( ContainerNode &node,
+                    pj_qos_params &qos) throw(Error)
+{
+    ContainerNode this_node = node.readContainer("qosParams");
+
+    NODE_READ_NUM_T( this_node, pj_uint8_t, qos.flags);
+    NODE_READ_NUM_T( this_node, pj_uint8_t, qos.dscp_val);
+    NODE_READ_NUM_T( this_node, pj_uint8_t, qos.so_prio);
+    NODE_READ_NUM_T( this_node, pj_qos_wmm_prio, qos.wmm_prio);
+}
+
+void writeQosParams( ContainerNode &node,
+                     const pj_qos_params &qos) throw(Error)
+{
+    ContainerNode this_node = node.writeNewContainer("qosParams");
+
+    NODE_WRITE_NUM_T( this_node, pj_uint8_t, qos.flags);
+    NODE_WRITE_NUM_T( this_node, pj_uint8_t, qos.dscp_val);
+    NODE_WRITE_NUM_T( this_node, pj_uint8_t, qos.so_prio);
+    NODE_WRITE_NUM_T( this_node, pj_qos_wmm_prio, qos.wmm_prio);
+}
+
+void readSipHeaders( const ContainerNode &node,
+                     const string &array_name,
+                     SipHeaderVector &headers) throw(Error)
+{
+    ContainerNode headers_node = node.readArray(array_name);
+    headers.resize(0);
+    while (headers_node.hasUnread()) {
+	SipHeader hdr;
+
+	ContainerNode header_node = headers_node.readContainer("header");
+	hdr.hName = header_node.readString("hname");
+	hdr.hValue = header_node.readString("hvalue");
+	headers.push_back(hdr);
+    }
+}
+
+void writeSipHeaders(ContainerNode &node,
+                     const string &array_name,
+                     const SipHeaderVector &headers) throw(Error)
+{
+    ContainerNode headers_node = node.writeNewArray(array_name);
+    for (unsigned i=0; i<headers.size(); ++i) {
+	ContainerNode header_node = headers_node.writeNewContainer("header");
+	header_node.writeString("hname", headers[i].hName);
+	header_node.writeString("hvalue", headers[i].hValue);
+    }
+}
+
+} // namespace
+///////////////////////////////////////////////////////////////////////////////
+
+AuthCredInfo::AuthCredInfo()
+: scheme("digest"), realm("*"), dataType(0)
+{
+}
+
+AuthCredInfo::AuthCredInfo(const string &param_scheme,
+			   const string &param_realm,
+			   const string &param_user_name,
+			   const int param_data_type,
+			   const string param_data)
+: scheme(param_scheme), realm(param_realm), username(param_user_name),
+  dataType(param_data_type), data(param_data)
+{
+}
+
+void AuthCredInfo::readObject(const ContainerNode &node) throw(Error)
+{
+    ContainerNode this_node = node.readContainer("AuthCredInfo");
+
+    NODE_READ_STRING( this_node, scheme);
+    NODE_READ_STRING( this_node, realm);
+    NODE_READ_STRING( this_node, username);
+    NODE_READ_INT   ( this_node, dataType);
+    NODE_READ_STRING( this_node, data);
+    NODE_READ_STRING( this_node, akaK);
+    NODE_READ_STRING( this_node, akaOp);
+    NODE_READ_STRING( this_node, akaAmf);
+}
+
+void AuthCredInfo::writeObject(ContainerNode &node) const throw(Error)
+{
+    ContainerNode this_node = node.writeNewContainer("AuthCredInfo");
+
+    NODE_WRITE_STRING( this_node, scheme);
+    NODE_WRITE_STRING( this_node, realm);
+    NODE_WRITE_STRING( this_node, username);
+    NODE_WRITE_INT   ( this_node, dataType);
+    NODE_WRITE_STRING( this_node, data);
+    NODE_WRITE_STRING( this_node, akaK);
+    NODE_WRITE_STRING( this_node, akaOp);
+    NODE_WRITE_STRING( this_node, akaAmf);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+TlsConfig::TlsConfig()
+{
+    pjsip_tls_setting ts;
+    pjsip_tls_setting_default(&ts);
+    this->fromPj(ts);
+}
+
+pjsip_tls_setting TlsConfig::toPj() const
+{
+    pjsip_tls_setting ts;
+
+    ts.ca_list_file	= str2Pj(this->CaListFile);
+    ts.cert_file	= str2Pj(this->certFile);
+    ts.privkey_file	= str2Pj(this->privKeyFile);
+    ts.password		= str2Pj(this->password);
+    ts.method		= this->method;
+    ts.ciphers_num	= this->ciphers.size();
+    // The following will only work if sizeof(enum)==sizeof(int)
+    pj_assert(sizeof(ts.ciphers[0]) == sizeof(int));
+    ts.ciphers		= ts.ciphers_num? 
+			    (pj_ssl_cipher*)&this->ciphers[0] : NULL;
+    ts.verify_server	= this->verifyServer;
+    ts.verify_client	= this->verifyClient;
+    ts.require_client_cert = this->requireClientCert;
+    ts.timeout.sec 	= this->msecTimeout / 1000;
+    ts.timeout.msec	= this->msecTimeout % 1000;
+    ts.qos_type		= this->qosType;
+    ts.qos_params	= this->qosParams;
+    ts.qos_ignore_error	= this->qosIgnoreError;
+
+    return ts;
+}
+
+void TlsConfig::fromPj(const pjsip_tls_setting &prm)
+{
+    this->CaListFile 	= pj2Str(prm.ca_list_file);
+    this->certFile 	= pj2Str(prm.cert_file);
+    this->privKeyFile 	= pj2Str(prm.privkey_file);
+    this->password 	= pj2Str(prm.password);
+    this->method 	= (pjsip_ssl_method)prm.method;
+    // The following will only work if sizeof(enum)==sizeof(int)
+    pj_assert(sizeof(prm.ciphers[0]) == sizeof(int));
+    this->ciphers 	= IntVector(prm.ciphers, prm.ciphers+prm.ciphers_num);
+    this->verifyServer 	= PJ2BOOL(prm.verify_server);
+    this->verifyClient 	= PJ2BOOL(prm.verify_client);
+    this->requireClientCert = PJ2BOOL(prm.require_client_cert);
+    this->msecTimeout 	= PJ_TIME_VAL_MSEC(prm.timeout);
+    this->qosType 	= prm.qos_type;
+    this->qosParams 	= prm.qos_params;
+    this->qosIgnoreError = PJ2BOOL(prm.qos_ignore_error);
+}
+
+void TlsConfig::readObject(const ContainerNode &node) throw(Error)
+{
+    ContainerNode this_node = node.readContainer("TlsConfig");
+
+    NODE_READ_STRING  ( this_node, CaListFile);
+    NODE_READ_STRING  ( this_node, certFile);
+    NODE_READ_STRING  ( this_node, privKeyFile);
+    NODE_READ_STRING  ( this_node, password);
+    NODE_READ_NUM_T   ( this_node, pjsip_ssl_method, method);
+    readIntVector     ( this_node, "ciphers", ciphers);
+    NODE_READ_BOOL    ( this_node, verifyServer);
+    NODE_READ_BOOL    ( this_node, verifyClient);
+    NODE_READ_BOOL    ( this_node, requireClientCert);
+    NODE_READ_UNSIGNED( this_node, msecTimeout);
+    NODE_READ_NUM_T   ( this_node, pj_qos_type, qosType);
+    readQosParams     ( this_node, qosParams);
+    NODE_READ_BOOL    ( this_node, qosIgnoreError);
+}
+
+void TlsConfig::writeObject(ContainerNode &node) const throw(Error)
+{
+    ContainerNode this_node = node.writeNewContainer("TlsConfig");
+
+    NODE_WRITE_STRING  ( this_node, CaListFile);
+    NODE_WRITE_STRING  ( this_node, certFile);
+    NODE_WRITE_STRING  ( this_node, privKeyFile);
+    NODE_WRITE_STRING  ( this_node, password);
+    NODE_WRITE_NUM_T   ( this_node, pjsip_ssl_method, method);
+    writeIntVector     ( this_node, "ciphers", ciphers);
+    NODE_WRITE_BOOL    ( this_node, verifyServer);
+    NODE_WRITE_BOOL    ( this_node, verifyClient);
+    NODE_WRITE_BOOL    ( this_node, requireClientCert);
+    NODE_WRITE_UNSIGNED( this_node, msecTimeout);
+    NODE_WRITE_NUM_T   ( this_node, pj_qos_type, qosType);
+    writeQosParams     ( this_node, qosParams);
+    NODE_WRITE_BOOL    ( this_node, qosIgnoreError);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+TransportConfig::TransportConfig()
+{
+    pjsua_transport_config tc;
+    pjsua_transport_config_default(&tc);
+    this->fromPj(tc);
+}
+
+void TransportConfig::fromPj(const pjsua_transport_config &prm)
+{
+    this->port 		= prm.port;
+    this->portRange	= prm.port_range;
+    this->publicAddress = pj2Str(prm.public_addr);
+    this->boundAddress	= pj2Str(prm.bound_addr);
+    this->tlsConfig.fromPj(prm.tls_setting);
+    this->qosType	= prm.qos_type;
+    this->qosParams	= prm.qos_params;
+}
+
+pjsua_transport_config TransportConfig::toPj() const
+{
+    pjsua_transport_config tc;
+
+    tc.port		= this->port;
+    tc.port_range	= this->portRange;
+    tc.public_addr	= str2Pj(this->publicAddress);
+    tc.bound_addr	= str2Pj(this->boundAddress);
+    tc.tls_setting	= this->tlsConfig.toPj();
+    tc.qos_type		= this->qosType;
+    tc.qos_params	= this->qosParams;
+
+    return tc;
+}
+
+void TransportConfig::readObject(const ContainerNode &node) throw(Error)
+{
+    ContainerNode this_node = node.readContainer("TransportConfig");
+
+    NODE_READ_UNSIGNED  ( this_node, port);
+    NODE_READ_UNSIGNED  ( this_node, portRange);
+    NODE_READ_STRING    ( this_node, publicAddress);
+    NODE_READ_STRING    ( this_node, boundAddress);
+    NODE_READ_NUM_T     ( this_node, pj_qos_type, qosType);
+    readQosParams       ( this_node, qosParams);
+    NODE_READ_OBJ       ( this_node, tlsConfig);
+}
+
+void TransportConfig::writeObject(ContainerNode &node) const throw(Error)
+{
+    ContainerNode this_node = node.writeNewContainer("TransportConfig");
+
+    NODE_WRITE_UNSIGNED  ( this_node, port);
+    NODE_WRITE_UNSIGNED  ( this_node, portRange);
+    NODE_WRITE_STRING    ( this_node, publicAddress);
+    NODE_WRITE_STRING    ( this_node, boundAddress);
+    NODE_WRITE_NUM_T     ( this_node, pj_qos_type, qosType);
+    writeQosParams       ( this_node, qosParams);
+    NODE_WRITE_OBJ       ( this_node, tlsConfig);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void TransportInfo::fromPj(const pjsua_transport_info &info)
+{
+    this->id = info.id;
+    this->type = info.type;
+    this->typeName = pj2Str(info.type_name);
+    this->info = pj2Str(info.info);
+    this->flags = info.flag;
+
+    char straddr[PJ_INET6_ADDRSTRLEN+10];
+    pj_sockaddr_print(&info.local_addr, straddr, sizeof(straddr), 3);
+    this->localAddress = straddr;
+
+    pj_ansi_snprintf(straddr, sizeof(straddr), "%.*s:%d",
+                     (int)info.local_name.host.slen,
+                     info.local_name.host.ptr,
+                     info.local_name.port);
+    this->localName = straddr;
+    this->usageCount = info.usage_count;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SipRxData::SipRxData()
+: pjRxData(NULL)
+{
+}
+
+void SipRxData::fromPj(pjsip_rx_data &rdata)
+{
+    char straddr[PJ_INET6_ADDRSTRLEN+10];
+
+    info	= pjsip_rx_data_get_info(&rdata);
+    wholeMsg	= string(rdata.msg_info.msg_buf, rdata.msg_info.len);
+    pj_sockaddr_print(&rdata.pkt_info.src_addr, straddr, sizeof(straddr), 3);
+    srcAddress  = straddr;
+    pjRxData    = (void *)&rdata;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SipMediaType::fromPj(const pjsip_media_type &prm)
+{
+    type	= pj2Str(prm.type);
+    subType	= pj2Str(prm.subtype);
+}
+
+pjsip_media_type SipMediaType::toPj() const
+{
+    pjsip_media_type pj_mt;
+    pj_bzero(&pj_mt, sizeof(pj_mt));
+    pj_mt.type	    = str2Pj(type);
+    pj_mt.subtype   = str2Pj(subType);
+    return pj_mt;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SipHeader::fromPj(const pjsip_hdr *hdr) throw(Error)
+{
+    char buf[256];
+
+    int len = pjsip_hdr_print_on((void*)hdr, buf, sizeof(buf)-1);
+    if (len <= 0)
+	PJSUA2_RAISE_ERROR(PJ_ETOOSMALL);
+    buf[len] = '\0';
+
+    char *pos = strchr(buf, ':');
+    if (!pos)
+	PJSUA2_RAISE_ERROR(PJSIP_EINVALIDHDR);
+
+    // Trim white space after header name
+    char *end_name = pos;
+    while (end_name>buf && pj_isspace(*(end_name-1))) --end_name;
+
+    // Trim whitespaces after colon
+    char *start_val = pos+1;
+    while (*start_val && pj_isspace(*start_val)) ++start_val;
+
+    hName = string(buf, end_name);
+    hValue = string(start_val);
+}
+
+pjsip_generic_string_hdr &SipHeader::toPj() const
+{
+    pj_str_t hname  = str2Pj(hName);
+    pj_str_t hvalue = str2Pj(hValue);
+
+    pjsip_generic_string_hdr_init2(&pjHdr, &hname, &hvalue);
+    return pjHdr;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SipMultipartPart::fromPj(const pjsip_multipart_part &prm) throw(Error)
+{
+    headers.clear();
+    pjsip_hdr* pj_hdr = prm.hdr.next;
+    while (pj_hdr != &prm.hdr) {
+	SipHeader sh;
+	sh.fromPj(pj_hdr);
+	headers.push_back(sh);
+	pj_hdr = pj_hdr->next;
+    }
+
+    if (!prm.body)
+	PJSUA2_RAISE_ERROR(PJ_EINVAL);
+    
+    contentType.fromPj(prm.body->content_type);
+    body = string((char*)prm.body->data, prm.body->len);
+}
+
+pjsip_multipart_part& SipMultipartPart::toPj() const
+{
+    pj_list_init(&pjMpp.hdr);
+    for (unsigned i = 0; i < headers.size(); i++) {
+	pjsip_generic_string_hdr& pj_hdr = headers[i].toPj();
+	pj_list_push_back(&pjMpp.hdr, &pj_hdr);
+    }
+
+    pj_bzero(&pjMsgBody, sizeof(pjMsgBody));
+    pjMsgBody.content_type  = contentType.toPj();
+    pjMsgBody.print_body    = &pjsip_print_text_body;
+    pjMsgBody.clone_data    = &pjsip_clone_text_data;
+    pjMsgBody.data	    = (void*)body.c_str();
+    pjMsgBody.len	    = body.size();
+    pjMpp.body = &pjMsgBody;
+
+    return pjMpp;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SipEvent::SipEvent()
+: type(PJSIP_EVENT_UNKNOWN), pjEvent(NULL)
+{
+}
+
+void SipEvent::fromPj(const pjsip_event &ev)
+{
+    type = ev.type;
+    if (type == PJSIP_EVENT_TIMER) {
+        body.timer.entry = ev.body.timer.entry;
+    } else if (type == PJSIP_EVENT_TSX_STATE) {
+        body.tsxState.prevState = (pjsip_tsx_state_e)
+        ev.body.tsx_state.prev_state;
+        body.tsxState.tsx.fromPj(*ev.body.tsx_state.tsx);
+        if (body.tsxState.type == PJSIP_EVENT_TX_MSG) {
+            if (ev.body.tsx_state.src.tdata)
+        	body.tsxState.src.tdata.fromPj(*ev.body.tsx_state.src.tdata);
+        } else if (body.tsxState.type == PJSIP_EVENT_RX_MSG) {
+            if (ev.body.tsx_state.src.rdata)
+        	body.tsxState.src.rdata.fromPj(*ev.body.tsx_state.src.rdata);
+        } else if (body.tsxState.type == PJSIP_EVENT_TRANSPORT_ERROR) {
+            body.tsxState.src.status = ev.body.tsx_state.src.status;
+        } else if (body.tsxState.type == PJSIP_EVENT_TIMER) {
+            body.tsxState.src.timer = ev.body.tsx_state.src.timer;
+        } else if (body.tsxState.type == PJSIP_EVENT_USER) {
+            body.tsxState.src.data = ev.body.tsx_state.src.data;
+        }
+    } else if (type == PJSIP_EVENT_TX_MSG) {
+	if (ev.body.tx_msg.tdata)
+	    body.txMsg.tdata.fromPj(*ev.body.tx_msg.tdata);
+    } else if (type == PJSIP_EVENT_RX_MSG) {
+	if (ev.body.rx_msg.rdata)
+	    body.rxMsg.rdata.fromPj(*ev.body.rx_msg.rdata);
+    } else if (type == PJSIP_EVENT_TRANSPORT_ERROR) {
+	if (ev.body.tx_error.tdata)
+	    body.txError.tdata.fromPj(*ev.body.tx_error.tdata);
+	if (ev.body.tx_error.tsx)
+	    body.txError.tsx.fromPj(*ev.body.tx_error.tsx);
+    } else if (type == PJSIP_EVENT_USER) {
+        body.user.user1 = ev.body.user.user1;
+        body.user.user2 = ev.body.user.user2;
+        body.user.user3 = ev.body.user.user3;
+        body.user.user4 = ev.body.user.user4;
+    }
+    pjEvent = (void *)&ev;
+}
+
+SipTxData::SipTxData()
+: pjTxData(NULL)
+{
+}
+
+void SipTxData::fromPj(pjsip_tx_data &tdata)
+{
+    char straddr[PJ_INET6_ADDRSTRLEN+10];
+    
+    info	= pjsip_tx_data_get_info(&tdata);
+    pjsip_tx_data_encode(&tdata);
+    wholeMsg	= string(tdata.buf.start, tdata.buf.end - tdata.buf.start);
+    if (pj_sockaddr_has_addr(&tdata.tp_info.dst_addr)) {
+	pj_sockaddr_print(&tdata.tp_info.dst_addr, straddr, sizeof(straddr), 3);
+	dstAddress  = straddr;
+    } else {
+	dstAddress = "";
+    }
+    pjTxData    = (void *)&tdata;
+}
+
+SipTransaction::SipTransaction()
+: role(PJSIP_ROLE_UAC), statusCode(0), pjTransaction(NULL)
+{
+}
+
+void SipTransaction::fromPj(pjsip_transaction &tsx)
+{
+    this->role          = tsx.role;
+    this->method        = pj2Str(tsx.method.name);
+    this->statusCode    = tsx.status_code;
+    this->statusText    = pj2Str(tsx.status_text);
+    if (tsx.last_tx)
+	this->lastTx.fromPj(*tsx.last_tx);
+    else
+	this->lastTx.pjTxData = NULL;
+    this->pjTransaction = (void *)&tsx;
+}
+
+bool SipTxOption::isEmpty() const
+{
+    return (targetUri == "" && headers.size() == 0 && contentType == "" &&
+            msgBody == "" && multipartContentType.type == "" &&
+            multipartContentType.subType == "" && multipartParts.size() == 0);
+}
+
+void SipTxOption::fromPj(const pjsua_msg_data &prm) throw(Error)
+{
+    targetUri = pj2Str(prm.target_uri);
+
+    headers.clear();
+    pjsip_hdr* pj_hdr = prm.hdr_list.next;
+    while (pj_hdr != &prm.hdr_list) {
+	SipHeader sh;
+	sh.fromPj(pj_hdr);
+	headers.push_back(sh);
+	pj_hdr = pj_hdr->next;
+    }
+
+    contentType = pj2Str(prm.content_type);
+    msgBody = pj2Str(prm.msg_body);
+    multipartContentType.fromPj(prm.multipart_ctype);
+
+    multipartParts.clear();
+    pjsip_multipart_part* pj_mp = prm.multipart_parts.next;
+    while (pj_mp != &prm.multipart_parts) {
+	SipMultipartPart smp;
+	smp.fromPj(*pj_mp);
+	multipartParts.push_back(smp);
+	pj_mp = pj_mp->next;
+    }
+}
+
+void SipTxOption::toPj(pjsua_msg_data &msg_data) const
+{
+    unsigned i;
+
+    pjsua_msg_data_init(&msg_data);
+
+    msg_data.target_uri = str2Pj(targetUri);
+
+    pj_list_init(&msg_data.hdr_list);
+    for (i = 0; i < headers.size(); i++) {
+	pjsip_generic_string_hdr& pj_hdr = headers[i].toPj();
+	pj_list_push_back(&msg_data.hdr_list, &pj_hdr);
+    }
+
+    msg_data.content_type = str2Pj(contentType);
+    msg_data.msg_body = str2Pj(msgBody);
+    msg_data.multipart_ctype = multipartContentType.toPj();
+
+    pj_list_init(&msg_data.multipart_parts);
+    for (i = 0; i < multipartParts.size(); i++) {
+	pjsip_multipart_part& pj_part = multipartParts[i].toPj();
+	pj_list_push_back(&msg_data.multipart_parts, &pj_part);
+    }
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+SendInstantMessageParam::SendInstantMessageParam()
+: contentType("text/plain"), content(""), userData(NULL)
+{
+}
+
+SendTypingIndicationParam::SendTypingIndicationParam()
+: isTyping(false)
+{
+}
Index: /pjproject/trunk/pjsip/src/pjsua2/types.cpp
===================================================================
--- /pjproject/trunk/pjsip/src/pjsua2/types.cpp	(revision 4704)
+++ /pjproject/trunk/pjsip/src/pjsua2/types.cpp	(revision 4704)
@@ -0,0 +1,95 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2013 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 <pjsua2/types.hpp>
+#include "util.hpp"
+
+using namespace pj;
+using namespace std;
+
+#define THIS_FILE	"types.cpp"
+
+///////////////////////////////////////////////////////////////////////////////
+
+Error::Error()
+: status(PJ_SUCCESS), srcLine(0)
+{
+}
+
+Error::Error( pj_status_t prm_status,
+	      const string &prm_title,
+	      const string &prm_reason,
+	      const string &prm_src_file,
+	      int prm_src_line)
+: status(prm_status), title(prm_title), reason(prm_reason),
+  srcFile(prm_src_file), srcLine(prm_src_line)
+{
+    if (this->status != PJ_SUCCESS && prm_reason.empty()) {
+	char errmsg[PJ_ERR_MSG_SIZE];
+	pj_strerror(this->status, errmsg, sizeof(errmsg));
+	this->reason = errmsg;
+    }
+}
+
+string Error::info(bool multi_line) const
+{
+    string output;
+
+    if (status==PJ_SUCCESS) {
+	output = "No error";
+    } else if (!multi_line) {
+	char temp[80];
+
+	if (!title.empty()) {
+	    output += title + " error: ";
+	}
+	snprintf(temp, sizeof(temp), " (status=%d)", status);
+	output += reason + temp;
+	if (!srcFile.empty()) {
+	    output += " [";
+	    output += srcFile;
+	    snprintf(temp, sizeof(temp), ":%d]", srcLine);
+	    output += temp;
+	}
+    } else {
+	char temp[80];
+
+	if (!title.empty()) {
+	    output += string("Title:       ") + title + "\n";
+	}
+
+	snprintf(temp, sizeof(temp), "%d\n", status);
+	output += string("Code:        ") + temp;
+	output += string("Description: ") + reason + "\n";
+	if (!srcFile.empty()) {
+	    snprintf(temp, sizeof(temp), ":%d\n", srcLine);
+	    output += string("Location:    ") + srcFile + temp;
+	}
+    }
+
+    return output;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void TimeValue::fromPj(const pj_time_val &prm)
+{
+    this->sec  = prm.sec;
+    this->msec = prm.msec;
+}
+
Index: /pjproject/trunk/pjsip/src/pjsua2/util.hpp
===================================================================
--- /pjproject/trunk/pjsip/src/pjsua2/util.hpp	(revision 4704)
+++ /pjproject/trunk/pjsip/src/pjsua2/util.hpp	(revision 4704)
@@ -0,0 +1,45 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2013 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 <pjsua2/types.hpp>
+#include <string>
+
+#define PJ2BOOL(var) ((var) != PJ_FALSE)
+
+namespace pj
+{
+using std::string;
+
+inline pj_str_t str2Pj(const string &input_str)
+{
+    pj_str_t output_str;
+    output_str.ptr = (char*)input_str.c_str();
+    output_str.slen = input_str.size();
+    return output_str;
+}
+
+inline string pj2Str(const pj_str_t &input_str)
+{
+    if (input_str.ptr)
+	return string(input_str.ptr, input_str.slen);
+    return string();
+}
+
+
+}	// namespace
