Initial sources of APS-direct.

git-svn-id: https://svn.pjsip.org/repos/pjproject/branches/projects/aps-direct@2434 74dad513-b988-da41-8d7b-12977e46ad98
diff --git a/build.symbian/bld.inf b/build.symbian/bld.inf
index b624905..4c9c13a 100644
--- a/build.symbian/bld.inf
+++ b/build.symbian/bld.inf
@@ -1,6 +1,3 @@
-#define SND_USE_NULL	0
-#define SND_USE_APS	0
-
 prj_platforms
 winscw
 //armv5
@@ -25,13 +22,7 @@
 libspeexcodec.mmp
 
 /* Sound device impl */
-#if SND_USE_NULL
-	null_audio.mmp
-#elif SND_USE_APS
-	symbian_audio_aps.mmp
-#else
-	symbian_audio.mmp
-#endif
+symbian_audio.mmp
 
 /* Applications */
 //pjlib_test.mmp
diff --git a/build.symbian/pjmedia.mmp b/build.symbian/pjmedia.mmp
index ce2d279..6c48b6c 100644
--- a/build.symbian/pjmedia.mmp
+++ b/build.symbian/pjmedia.mmp
@@ -40,6 +40,7 @@
 SOURCE		clock_thread.c
 SOURCE		codec.c
 SOURCE		conference.c
+SOURCE		conf_switch.c
 SOURCE		echo_common.c
 SOURCE		echo_port.c
 SOURCE		echo_suppress.c
diff --git a/build.symbian/symbian_audio.mmp b/build.symbian/symbian_audio.mmp
index 571b4b0..e53f4e8 100644
--- a/build.symbian/symbian_audio.mmp
+++ b/build.symbian/symbian_audio.mmp
@@ -24,22 +24,23 @@
 SOURCEPATH	..\pjmedia\src\pjmedia
 
 OPTION		CW -lang c++
-
-//
-// GCCE optimization setting
-//
 OPTION		GCCE -O2 -fno-unit-at-a-time
 
 MACRO		PJ_M_I386=1
 MACRO		PJ_SYMBIAN=1
 
+SOURCE		nullsound.c
 SOURCE		symbian_sound.cpp
+SOURCE		symbian_sound_aps.cpp
 
 SYSTEMINCLUDE	..\pjlib\include
 SYSTEMINCLUDE	..\pjmedia\include
 
 SYSTEMINCLUDE	\epoc32\include
 SYSTEMINCLUDE	\epoc32\include\libc
+SYSTEMINCLUDE   \epoc32\include\mmf\server 
+SYSTEMINCLUDE   \epoc32\include\mmf\common 
+SYSTEMINCLUDE   \epoc32\include\mda\common 
 
 SYSTEMINCLUDE	\epoc32\include\mmf\plugin 
 
diff --git a/build.symbian/symbian_ua.mmp b/build.symbian/symbian_ua.mmp
index 938b84d..5f7f1f8 100644
--- a/build.symbian/symbian_ua.mmp
+++ b/build.symbian/symbian_ua.mmp
@@ -1,63 +1,61 @@
-#define SND_USE_NULL	0

-#define SND_USE_APS	0

-

-TARGET 		symbian_ua.exe

-TARGETTYPE 	exe

-UID		0x0 0xA000000D

-

-SOURCEPATH	..\pjsip-apps\src\symbian_ua

-

-MACRO		PJ_M_I386=1

-MACRO		PJ_SYMBIAN=1

-

-// Source files

-

-SOURCE	ua.cpp

-SOURCE	main_symbian.cpp

-

-DOCUMENT ua.h

-

-START RESOURCE  symbian_ua_reg.rss

-	TARGETPATH      \private\10003a3f\apps

-END

-

-SYSTEMINCLUDE	..\pjlib\include

-SYSTEMINCLUDE	..\pjlib-util\include

-SYSTEMINCLUDE	..\pjnath\include

-SYSTEMINCLUDE	..\pjmedia\include

-SYSTEMINCLUDE	..\pjsip\include

-

-SYSTEMINCLUDE	\epoc32\include

-SYSTEMINCLUDE	\epoc32\include\libc

-

-STATICLIBRARY	pjsua_lib.lib pjsip_ua.lib

-STATICLIBRARY	pjsip_simple.lib pjsip.lib pjsdp.lib pjmedia.lib

-STATICLIBRARY	pjnath.lib pjlib_util.lib pjlib.lib

-STATICLIBRARY	libsrtp.lib

-STATICLIBRARY	libgsmcodec.lib libspeexcodec.lib

-

-#if SND_USE_NULL

-    STATICLIBRARY	null_audio.lib

-    CAPABILITY		NetworkServices LocalServices ReadUserData WriteUserData UserEnvironment 

-#elif SND_USE_APS

-    STATICLIBRARY	symbian_audio_aps.lib

-    LIBRARY		APSSession2.lib

-    CAPABILITY		NetworkServices LocalServices ReadUserData WriteUserData UserEnvironment MultimediaDD

-    MACRO		PJMEDIA_SYM_SND_USE_APS=1

-#else

-    STATICLIBRARY	symbian_audio.lib

-    LIBRARY 		mediaclientaudiostream.lib

-    LIBRARY 		mediaclientaudioinputstream.lib

-    CAPABILITY		NetworkServices LocalServices ReadUserData WriteUserData UserEnvironment 

-#endif

-

-#ifdef WINSCW

-	STATICLIBRARY   eexe.lib ecrt0.lib

-#endif

-

-LIBRARY		esock.lib insock.lib charconv.lib euser.lib estlib.lib commdb.lib apengine.lib 

-

-// The default 8KB seems to be insufficient with all bells and

-// whistles turned on

-EPOCSTACKSIZE	12288

-

+#define SND_USE_APS	1
+#define SND_USE_VAS	0
+
+TARGET 			symbian_ua.exe
+TARGETTYPE 		exe
+UID			0x0 0xA000000D
+
+SOURCEPATH		..\pjsip-apps\src\symbian_ua
+
+MACRO			PJ_M_I386=1
+MACRO			PJ_SYMBIAN=1
+
+// Source files
+
+SOURCE			ua.cpp
+SOURCE			main_symbian.cpp
+
+DOCUMENT 		ua.h
+
+START RESOURCE  	symbian_ua_reg.rss
+	TARGETPATH      \private\10003a3f\apps
+END
+
+SYSTEMINCLUDE		..\pjlib\include
+SYSTEMINCLUDE		..\pjlib-util\include
+SYSTEMINCLUDE		..\pjnath\include
+SYSTEMINCLUDE		..\pjmedia\include
+SYSTEMINCLUDE		..\pjsip\include
+
+SYSTEMINCLUDE		\epoc32\include
+SYSTEMINCLUDE		\epoc32\include\libc
+
+STATICLIBRARY		pjsua_lib.lib pjsip_ua.lib
+STATICLIBRARY		pjsip_simple.lib pjsip.lib pjsdp.lib pjmedia.lib
+STATICLIBRARY		pjnath.lib pjlib_util.lib pjlib.lib
+STATICLIBRARY		libsrtp.lib
+STATICLIBRARY		libgsmcodec.lib libspeexcodec.lib
+STATICLIBRARY		symbian_audio.lib
+
+#if SND_USE_APS
+	LIBRARY		APSSession2.lib
+	CAPABILITY	NetworkServices LocalServices ReadUserData WriteUserData UserEnvironment MultimediaDD
+#elif SND_USE_VAS
+//	LIBRARY		
+	CAPABILITY	NetworkServices LocalServices ReadUserData WriteUserData UserEnvironment MultimediaDD
+#else
+	LIBRARY 	mediaclientaudiostream.lib
+	LIBRARY 	mediaclientaudioinputstream.lib
+	CAPABILITY	NetworkServices LocalServices ReadUserData WriteUserData UserEnvironment
+#endif
+
+#ifdef WINSCW
+	STATICLIBRARY   eexe.lib ecrt0.lib
+#endif
+
+LIBRARY			esock.lib insock.lib charconv.lib euser.lib estlib.lib commdb.lib apengine.lib 
+
+// The default 8KB seems to be insufficient with all bells and
+// whistles turned on
+EPOCSTACKSIZE	12288
+
diff --git a/build.symbian/symsndtest.mmp b/build.symbian/symsndtest.mmp
index a08f1d8..470de9d 100644
--- a/build.symbian/symsndtest.mmp
+++ b/build.symbian/symsndtest.mmp
@@ -1,53 +1,45 @@
-#define SND_USE_NULL	0
-#define SND_USE_APS	0
+#define SND_USE_APS	1
+#define SND_USE_VAS	0
 
-TARGET 		symsndtest.exe
-TARGETTYPE 	exe
-UID		0x0 0xA000000E
+TARGET 			symsndtest.exe
+TARGETTYPE 		exe
+UID			0x0 0xA000000E
 
-SOURCEPATH	..\pjsip-apps\src\symsndtest
+SOURCEPATH		..\pjsip-apps\src\symsndtest
 
-MACRO		PJ_M_I386=1
-MACRO		PJ_SYMBIAN=1
+MACRO			PJ_M_I386=1
+MACRO			PJ_SYMBIAN=1
 
 // Test files
 
-SOURCE	app_main.cpp
-SOURCE	main_symbian.cpp
+SOURCE			app_main.cpp
+SOURCE			main_symbian.cpp
 
-START RESOURCE  symsndtest_reg.rss
+START RESOURCE  	symsndtest_reg.rss
 	TARGETPATH	\private\10003a3f\apps
 END
 
-SYSTEMINCLUDE	..\pjlib\include
-SYSTEMINCLUDE	..\pjmedia\include
+SYSTEMINCLUDE		..\pjlib\include
+SYSTEMINCLUDE		..\pjmedia\include
 
-SYSTEMINCLUDE	\epoc32\include
-SYSTEMINCLUDE	\epoc32\include\libc
+SYSTEMINCLUDE		\epoc32\include
+SYSTEMINCLUDE		\epoc32\include\libc
 
-LIBRARY		charconv.lib euser.lib estlib.lib
-LIBRARY		esock.lib insock.lib
-STATICLIBRARY	pjlib.lib pjmedia.lib
+LIBRARY			charconv.lib euser.lib estlib.lib
+LIBRARY			esock.lib insock.lib
+STATICLIBRARY		pjlib.lib pjmedia.lib
+STATICLIBRARY		symbian_audio.lib
 
-#if SND_USE_NULL
-    STATICLIBRARY	null_audio.lib
-    CAPABILITY		NetworkServices LocalServices ReadUserData WriteUserData UserEnvironment 
-#elif SND_USE_APS
-    SOURCEPATH                              ..\pjmedia\src\pjmedia 
-    SOURCE                                  symbian_sound_aps.cpp 
-
-    SYSTEMINCLUDE                           \epoc32\include\mmf\server 
-    SYSTEMINCLUDE                           \epoc32\include\mmf\common 
-    SYSTEMINCLUDE                           \epoc32\include\mda\common 
-
-    //STATICLIBRARY	symbian_audio_aps.lib
-    LIBRARY		APSSession2.lib
-    CAPABILITY		NetworkServices LocalServices ReadUserData WriteUserData UserEnvironment MultimediaDD
+#if SND_USE_APS
+	LIBRARY		APSSession2.lib
+	CAPABILITY	NetworkServices LocalServices ReadUserData WriteUserData UserEnvironment MultimediaDD
+#elif SND_USE_VAS
+//	LIBRARY		
+	CAPABILITY	NetworkServices LocalServices ReadUserData WriteUserData UserEnvironment MultimediaDD
 #else
-    STATICLIBRARY	symbian_audio.lib
-    LIBRARY 		mediaclientaudiostream.lib
-    LIBRARY 		mediaclientaudioinputstream.lib
-    CAPABILITY		NetworkServices LocalServices ReadUserData WriteUserData UserEnvironment 
+	LIBRARY 	mediaclientaudiostream.lib
+	LIBRARY 	mediaclientaudioinputstream.lib
+	CAPABILITY	NetworkServices LocalServices ReadUserData WriteUserData UserEnvironment
 #endif
 
 #ifdef WINSCW
diff --git a/pjmedia/build/pjmedia.vcproj b/pjmedia/build/pjmedia.vcproj
index d51f300..2add52d 100644
--- a/pjmedia/build/pjmedia.vcproj
+++ b/pjmedia/build/pjmedia.vcproj
@@ -95,78 +95,6 @@
 			/>

 		</Configuration>

 		<Configuration

-			Name="Release|Windows Mobile 6 Standard SDK (ARMV4I)"

-			OutputDirectory="output\$(ProjectName)-$(PlatformName)-$(ConfigurationName)"

-			IntermediateDirectory="$(OutDir)"

-			ConfigurationType="4"

-			CharacterSet="1"

-			>

-			<Tool

-				Name="VCPreBuildEventTool"

-			/>

-			<Tool

-				Name="VCCustomBuildTool"

-			/>

-			<Tool

-				Name="VCXMLDataGeneratorTool"

-			/>

-			<Tool

-				Name="VCWebServiceProxyGeneratorTool"

-			/>

-			<Tool

-				Name="VCMIDLTool"

-			/>

-			<Tool

-				Name="VCCLCompilerTool"

-				ExecutionBucket="7"

-				Optimization="2"

-				FavorSizeOrSpeed="2"

-				AdditionalIncludeDirectories="../include,../../pjlib/include,../../pjlib-util/include,../../pjnath/include,../../third_party/portaudio/include,../../third_party/speex/include,../../third_party/build/srtp,../../third_party/srtp/include,../../third_party/srtp/crypto/include;../.."

-				PreprocessorDefinitions="NDEBUG;_WIN32_WCE=$(CEVER);UNDER_CE;$(PLATFORMDEFINES);WINCE;$(ARCHFAM);$(_ARCHFAM_);SMARTPHONE2003_UI_MODEL;SMARTPHONE2003_UI_MODEL"

-				ExceptionHandling="0"

-				RuntimeLibrary="0"

-				WarningLevel="3"

-				DebugInformationFormat="0"

-				CompileAs="0"

-			/>

-			<Tool

-				Name="VCManagedResourceCompilerTool"

-			/>

-			<Tool

-				Name="VCResourceCompilerTool"

-			/>

-			<Tool

-				Name="VCPreLinkEventTool"

-			/>

-			<Tool

-				Name="VCLibrarianTool"

-				AdditionalOptions=""

-			/>

-			<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"

 			OutputDirectory=".\output\pjmedia-i386-win32-vc8-debug"

 			IntermediateDirectory=".\output\pjmedia-i386-win32-vc8-debug"

@@ -243,6 +171,78 @@
 			/>

 		</Configuration>

 		<Configuration

+			Name="Release|Windows Mobile 6 Standard SDK (ARMV4I)"

+			OutputDirectory="output\$(ProjectName)-$(PlatformName)-$(ConfigurationName)"

+			IntermediateDirectory="$(OutDir)"

+			ConfigurationType="4"

+			CharacterSet="1"

+			>

+			<Tool

+				Name="VCPreBuildEventTool"

+			/>

+			<Tool

+				Name="VCCustomBuildTool"

+			/>

+			<Tool

+				Name="VCXMLDataGeneratorTool"

+			/>

+			<Tool

+				Name="VCWebServiceProxyGeneratorTool"

+			/>

+			<Tool

+				Name="VCMIDLTool"

+			/>

+			<Tool

+				Name="VCCLCompilerTool"

+				ExecutionBucket="7"

+				Optimization="2"

+				FavorSizeOrSpeed="2"

+				AdditionalIncludeDirectories="../include,../../pjlib/include,../../pjlib-util/include,../../pjnath/include,../../third_party/portaudio/include,../../third_party/speex/include,../../third_party/build/srtp,../../third_party/srtp/include,../../third_party/srtp/crypto/include;../.."

+				PreprocessorDefinitions="NDEBUG;_WIN32_WCE=$(CEVER);UNDER_CE;$(PLATFORMDEFINES);WINCE;$(ARCHFAM);$(_ARCHFAM_);SMARTPHONE2003_UI_MODEL;SMARTPHONE2003_UI_MODEL"

+				ExceptionHandling="0"

+				RuntimeLibrary="0"

+				WarningLevel="3"

+				DebugInformationFormat="0"

+				CompileAs="0"

+			/>

+			<Tool

+				Name="VCManagedResourceCompilerTool"

+			/>

+			<Tool

+				Name="VCResourceCompilerTool"

+			/>

+			<Tool

+				Name="VCPreLinkEventTool"

+			/>

+			<Tool

+				Name="VCLibrarianTool"

+				AdditionalOptions=""

+			/>

+			<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)"

 			OutputDirectory="output\$(ProjectName)-$(PlatformName)-$(ConfigurationName)"

 			IntermediateDirectory="$(OutDir)"

@@ -434,6 +434,10 @@
 				</FileConfiguration>

 			</File>

 			<File

+				RelativePath="..\src\pjmedia\conf_switch.c"

+				>

+			</File>

+			<File

 				RelativePath="..\src\pjmedia\conference.c"

 				>

 				<FileConfiguration

diff --git a/pjmedia/include/pjmedia-codec/config.h b/pjmedia/include/pjmedia-codec/config.h
index e50185d..40376d1 100644
--- a/pjmedia/include/pjmedia-codec/config.h
+++ b/pjmedia/include/pjmedia-codec/config.h
@@ -194,5 +194,13 @@
 #   define PJMEDIA_HAS_INTEL_IPP_CODEC_G722_1	1
 #endif
 
+/**
+ * Enable Passthrough codecs.
+ *
+ * Default: 0
+ */
+#ifndef PJMEDIA_HAS_PASSTHROUGH_CODECS
+#   define PJMEDIA_HAS_PASSTHROUGH_CODECS	0
+#endif
 
 #endif	/* __PJMEDIA_CODEC_CONFIG_H__ */
diff --git a/pjmedia/include/pjmedia/conference.h b/pjmedia/include/pjmedia/conference.h
index bc14c54..a6ffb3d 100644
--- a/pjmedia/include/pjmedia/conference.h
+++ b/pjmedia/include/pjmedia/conference.h
@@ -56,6 +56,7 @@
 {
     unsigned		slot;		    /**< Slot number.		    */
     pj_str_t		name;		    /**< Port name.		    */
+    pjmedia_fourcc	format;		    /**< Format (FourCC identifier) */
     pjmedia_port_op	tx_setting;	    /**< Transmit settings.	    */
     pjmedia_port_op	rx_setting;	    /**< Receive settings.	    */
     unsigned		listener_cnt;	    /**< Number of listeners.	    */
diff --git a/pjmedia/include/pjmedia/config.h b/pjmedia/include/pjmedia/config.h
index 735192e..de6e680 100644
--- a/pjmedia/include/pjmedia/config.h
+++ b/pjmedia/include/pjmedia/config.h
@@ -44,6 +44,22 @@
 #   include <pjmedia/config_auto.h>
 #endif
 
+/**
+ * Specify whether we prefer to use audio switch board rather than 
+ * conference bridge.
+ *
+ * Audio switch board is a kind of simplified version of conference 
+ * bridge, but not really the subset of conference bridge. It has 
+ * stricter rules on audio routing among the pjmedia ports and has
+ * no audio mixing capability. The power of it is it could work with
+ * encoded audio frames where conference brigde couldn't.
+ *
+ * Default: 0
+ */
+#ifndef PJMEDIA_CONF_USE_SWITCH_BOARD
+#   define PJMEDIA_CONF_USE_SWITCH_BOARD    0
+#endif
+
 /*
  * Types of sound stream backends.
  */
@@ -60,6 +76,16 @@
 /** Constant for Win32 MME sound backend. */
 #define PJMEDIA_SOUND_WIN32_MME_SOUND	    3
 
+/** Constant for Symbian Multimedia Audio Stream backend. */
+#define PJMEDIA_SOUND_SYMB_MDA_SOUND	    4
+
+/** Constant for Symbian APS backend. */
+#define PJMEDIA_SOUND_SYMB_APS_SOUND	    5
+
+/** Constant for Symbian VAS backend. */
+#define PJMEDIA_SOUND_SYMB_VAS_SOUND	    6
+
+
 /** When this is set, pjmedia will not provide any sound device backend. 
  *  Application will have to provide its own sound device backend
  *  and link the application with it.
diff --git a/pjmedia/include/pjmedia/port.h b/pjmedia/include/pjmedia/port.h
index 9d7c86c..245ef00 100644
--- a/pjmedia/include/pjmedia/port.h
+++ b/pjmedia/include/pjmedia/port.h
@@ -25,6 +25,7 @@
  * @brief Port interface declaration
  */
 #include <pjmedia/types.h>
+#include <pj/assert.h>
 #include <pj/os.h>
 
 
@@ -211,6 +212,7 @@
     pj_bool_t	    has_info;		/**< Has info?			    */
     pj_bool_t	    need_info;		/**< Need info on connect?	    */
     unsigned	    pt;			/**< Payload type (can be dynamic). */
+    pjmedia_fourcc  format;		/**< Format (FourCC identifier)	    */
     pj_str_t	    encoding_name;	/**< Encoding name.		    */
     unsigned	    clock_rate;		/**< Sampling rate.		    */
     unsigned	    channel_count;	/**< Number of channels.	    */
@@ -226,7 +228,8 @@
 typedef enum pjmedia_frame_type
 {
     PJMEDIA_FRAME_TYPE_NONE,	    /**< No frame.		*/
-    PJMEDIA_FRAME_TYPE_AUDIO	    /**< Normal audio frame.	*/
+    PJMEDIA_FRAME_TYPE_AUDIO,	    /**< Normal audio frame.	*/
+    PJMEDIA_FRAME_TYPE_EXTENDED	    /**< Extended audio frame.	*/
 
 } pjmedia_frame_type;
 
@@ -249,6 +252,88 @@
 
 
 /**
+ * The pjmedia_frame_ext is used to carry a more complex audio frames than
+ * the typical PCM audio frames, and it is signaled by setting the "type"
+ * field of a pjmedia_frame to PJMEDIA_FRAME_TYPE_EXTENDED. With this set,
+ * application may typecast pjmedia_frame to pjmedia_frame_ext.
+ *
+ * This structure may contain more than one audio frames, which subsequently
+ * will be called subframes in this structure. The subframes section
+ * immediately follows the end of this structure, and each subframe is
+ * represented by pjmedia_frame_ext_subframe structure. Every next
+ * subframe immediately follows the previous subframe, and all subframes
+ * are byte-aligned although its payload may not be byte-aligned.
+ */
+typedef struct pjmedia_frame_ext {
+    pjmedia_frame   base;	    /**< Base frame info */
+    pj_uint16_t     samples_cnt;    /**< Number of samples in this frame */
+    pj_uint16_t     subframe_cnt;   /**< Number of (sub)frames in this frame */
+
+    /* Zero or more (sub)frames follows immediately after this,
+     * each will be represented by pjmedia_frame_ext_subframe
+     */
+} pjmedia_frame_ext;
+
+/**
+ * This structure represents the individual subframes in the
+ * pjmedia_frame_ext structure.
+ */
+typedef struct pjmedia_frame_ext_subframe {
+    pj_uint16_t     bitlen;	    /**< Number of bits in the data */
+    pj_uint8_t      data[1];	    /**< Start of encoded data */
+} pjmedia_frame_ext_subframe;
+
+
+/* Append one subframe to the frame_ext */
+PJ_INLINE(void) pjmedia_frame_ext_append_subframe(pjmedia_frame_ext *frm,
+						  const void *src,
+					          pj_uint16_t bitlen,
+						  pj_uint16_t samples_cnt)
+{
+    pj_uint8_t *p;
+    unsigned i, tmp;
+
+    p = (pj_uint8_t*)frm + sizeof(pjmedia_frame_ext);
+    for (i = 0; i < frm->subframe_cnt; ++i) {
+	pjmedia_frame_ext_subframe *fsub;
+	fsub = (pjmedia_frame_ext_subframe*) p;
+	p += fsub->bitlen / 8;
+	if (fsub->bitlen % 8)
+	    ++p;
+    }
+
+    tmp = bitlen / 8;
+    if (bitlen % 8) ++tmp;
+    pj_memcpy(p, &bitlen, sizeof(bitlen));
+    pj_memcpy(p + sizeof(bitlen), src, tmp);
+    frm->subframe_cnt++;
+    frm->samples_cnt = frm->samples_cnt + samples_cnt;
+}
+
+/* Get the pointer and length of the n-th subframe */
+PJ_INLINE(pjmedia_frame_ext_subframe*) 
+	  pjmedia_frame_ext_get_subframe(const pjmedia_frame_ext *frm,
+					 unsigned n)
+{
+    pj_uint8_t *p;
+    unsigned i;
+    pjmedia_frame_ext_subframe *tmp;
+
+    pj_assert(n < frm->subframe_cnt);
+
+    p = (pj_uint8_t*)frm + sizeof(pjmedia_frame_ext);
+    for (i = 0; i < n; ++i) {	
+	tmp = (pjmedia_frame_ext_subframe*) p;
+	p += tmp->bitlen / 8;
+	if (tmp->bitlen % 8)
+	    ++p;
+    }
+    
+    tmp = (pjmedia_frame_ext_subframe*) p;
+    return tmp;
+}
+	
+/**
  * Port interface.
  */
 typedef struct pjmedia_port
diff --git a/pjmedia/include/pjmedia/symbian_sound_aps.h b/pjmedia/include/pjmedia/symbian_sound_aps.h
index 3208b41..4d7c8ee 100644
--- a/pjmedia/include/pjmedia/symbian_sound_aps.h
+++ b/pjmedia/include/pjmedia/symbian_sound_aps.h
@@ -31,6 +31,24 @@
 PJ_BEGIN_DECL
 
 /**
+ * Declaration of APS sound setting.  
+ */
+typedef struct pjmedia_snd_aps_setting
+{
+    pjmedia_fourcc	format;	  /**< Format (FourCC ID).	*/ 
+    pj_uint32_t		bitrate;  /**< Bitrate (bps).		*/
+    pj_uint32_t		mode;	  /**< Mode, currently only used 
+				       for specifying iLBC mode,
+				       20ms or 30ms frame size.	*/
+    pj_bool_t		plc;	  /**< PLC enabled/disabled.	*/
+    pj_bool_t		vad;	  /**< VAD enabled/disabled.	*/
+    pj_bool_t		cng;	  /**< CNG enabled/disabled.	*/
+    pj_bool_t		loudspk;  /**< Audio routed to loudspeaker.*/
+    
+} pjmedia_snd_aps_setting;
+
+
+/**
  * Activate/deactivate loudspeaker, when loudspeaker is inactive, audio
  * will be routed to earpiece.
  *
@@ -47,6 +65,17 @@
 						pj_bool_t active);
 
 
+/**
+ * Set a codec and its settings to be used on the next sound device session.
+ *
+ * @param setting	APS sound device setting, see @pjmedia_snd_aps_setting.
+ *
+ * @return		PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjmedia_snd_aps_modify_setting(
+				    const pjmedia_snd_aps_setting *setting);
+
+
 PJ_END_DECL
 
 
diff --git a/pjmedia/include/pjmedia/types.h b/pjmedia/include/pjmedia/types.h
index e456e34..14786ca 100644
--- a/pjmedia/include/pjmedia/types.h
+++ b/pjmedia/include/pjmedia/types.h
@@ -47,8 +47,8 @@
  * @{
  */
 
-/** 
- * Top most media type. 
+/**
+ * Top most media type.
  */
 typedef enum pjmedia_type
 {
@@ -61,7 +61,7 @@
     /** The media is video. */
     PJMEDIA_TYPE_VIDEO = 2,
 
-    /** Unknown media type, in this case the name will be specified in 
+    /** Unknown media type, in this case the name will be specified in
      *  encoding_name.
      */
     PJMEDIA_TYPE_UNKNOWN = 3,
@@ -72,8 +72,8 @@
 } pjmedia_type;
 
 
-/** 
- * Media transport protocol. 
+/**
+ * Media transport protocol.
  */
 typedef enum pjmedia_tp_proto
 {
@@ -92,8 +92,8 @@
 } pjmedia_tp_proto;
 
 
-/** 
- * Media direction. 
+/**
+ * Media direction.
  */
 typedef enum pjmedia_dir
 {
@@ -138,8 +138,8 @@
 	    (a<<24 | b<<16 | c<<8 | d)
 
 
-/** 
- * Opague declaration of media endpoint. 
+/**
+ * Opaque declaration of media endpoint.
  */
 typedef struct pjmedia_endpt pjmedia_endpt;
 
@@ -150,7 +150,7 @@
 typedef struct pjmedia_stream pjmedia_stream;
 
 
-/** 
+/**
  * Media socket info is used to describe the underlying sockets
  * to be used as media transport.
  */
@@ -178,10 +178,34 @@
 
 } pjmedia_sock_info;
 
+/**
+ * Declaration of FourCC type.
+ */
+typedef union pjmedia_fourcc {
+   pj_uint32_t  u32;
+   char         c[4];
+} pjmedia_fourcc;
+
+
+/**
+ * FourCC packing macro.
+ */
+#define PJMEDIA_FOURCC_PACK(C1, C2, C3, C4) ( C1<<24 | C2<<16 | C3<<8 | C4 )
+
+/**
+ * FourCC identifier definitions.
+ */
+#define PJMEDIA_FOURCC_L16	PJMEDIA_FOURCC_PACK(' ', 'L', '1', '6')
+#define PJMEDIA_FOURCC_G711A	PJMEDIA_FOURCC_PACK('G', '7', '1', '1')
+#define PJMEDIA_FOURCC_G711U	PJMEDIA_FOURCC_PACK('U', 'L', 'A', 'W')
+#define PJMEDIA_FOURCC_AMR	PJMEDIA_FOURCC_PACK(' ', 'A', 'M', 'R')
+#define PJMEDIA_FOURCC_G729	PJMEDIA_FOURCC_PACK('G', '7', '2', '9')
+#define PJMEDIA_FOURCC_ILBC	PJMEDIA_FOURCC_PACK('i', 'L', 'B', 'C')
+
 
 /**
  * This is a general purpose function set PCM samples to zero.
- * Since this function is needed by many parts of the library, 
+ * Since this function is needed by many parts of the library,
  * by putting this functionality in one place, it enables some.
  * clever people to optimize this function.
  *
@@ -205,7 +229,7 @@
 
 /**
  * This is a general purpose function to copy samples from/to buffers with
- * equal size. Since this function is needed by many parts of the library, 
+ * equal size. Since this function is needed by many parts of the library,
  * by putting this functionality in one place, it enables some.
  * clever people to optimize this function.
  */
@@ -220,7 +244,7 @@
 #else
     unsigned i;
     count >>= 1;
-    for (i=0; i<count; ++i) 
+    for (i=0; i<count; ++i)
 	((pj_int32_t*)dst)[i] = ((pj_int32_t*)src)[i];
 #endif
 }
@@ -228,7 +252,7 @@
 
 /**
  * This is a general purpose function to copy samples from/to buffers with
- * equal size. Since this function is needed by many parts of the library, 
+ * equal size. Since this function is needed by many parts of the library,
  * by putting this functionality in one place, it enables some.
  * clever people to optimize this function.
  */
@@ -243,7 +267,7 @@
 #else
     unsigned i;
     count >>= 1;
-    for (i=0; i<count; ++i) 
+    for (i=0; i<count; ++i)
 	((pj_int32_t*)dst)[i] = ((pj_int32_t*)src)[i];
 #endif
 }
diff --git a/pjmedia/src/pjmedia/conf_switch.c b/pjmedia/src/pjmedia/conf_switch.c
new file mode 100644
index 0000000..5534076
--- /dev/null
+++ b/pjmedia/src/pjmedia/conf_switch.c
@@ -0,0 +1,1240 @@
+/* $Id$ */
+/* 
+ * Copyright (C) 2008-2009 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 
+ */
+#include <pjmedia/conference.h>
+#include <pjmedia/errno.h>
+#include <pjmedia/port.h>
+#include <pjmedia/sound_port.h>
+#include <pjmedia/stream.h>
+#include <pj/array.h>
+#include <pj/assert.h>
+#include <pj/log.h>
+#include <pj/pool.h>
+#include <pj/string.h>
+
+#if defined(PJMEDIA_CONF_USE_SWITCH_BOARD) && PJMEDIA_CONF_USE_SWITCH_BOARD!=0
+
+/* CONF_DEBUG enables detailed operation of the conference bridge.
+ * Beware that it prints large amounts of logs (several lines per frame).
+ */
+//#define CONF_DEBUG
+#ifdef CONF_DEBUG
+#   include <stdio.h>
+#   define TRACE_(x)   PJ_LOG(5,x)
+#else
+#   define TRACE_(x)
+#endif
+
+
+/* REC_FILE macro enables recording of the samples written to the sound
+ * device. The file contains RAW PCM data with no header, and has the
+ * same settings (clock rate etc) as the conference bridge.
+ * This should only be enabled when debugging audio quality *only*.
+ */
+//#define REC_FILE    "confrec.pcm"
+#ifdef REC_FILE
+static FILE *fhnd_rec;
+#endif
+
+
+#define THIS_FILE	    "conf_switch.c"
+
+#define SIGNATURE	    PJMEDIA_PORT_SIGNATURE('S', 'W', 'T', 'C')
+#define SIGNATURE_PORT	    PJMEDIA_PORT_SIGNATURE('S', 'W', 'T', 'P')
+#define NORMAL_LEVEL	    128
+#define SLOT_TYPE	    unsigned
+#define INVALID_SLOT	    ((SLOT_TYPE)-1)
+#define BUFFER_SIZE	    PJMEDIA_MAX_MTU
+
+/*
+ * DON'T GET CONFUSED WITH TX/RX!!
+ *
+ * TX and RX directions are always viewed from the conference bridge's point
+ * of view, and NOT from the port's point of view. So TX means the bridge
+ * is transmitting to the port, RX means the bridge is receiving from the
+ * port.
+ */
+
+
+/**
+ * This is a port connected to conference bridge.
+ */
+struct conf_port
+{
+    pj_str_t		 name;		/**< Port name.			    */
+    pjmedia_port	*port;		/**< get_frame() and put_frame()    */
+    pjmedia_port_op	 rx_setting;	/**< Can we receive from this port  */
+    pjmedia_port_op	 tx_setting;	/**< Can we transmit to this port   */
+    unsigned		 listener_cnt;	/**< Number of listeners.	    */
+    SLOT_TYPE		*listener_slots;/**< Array of listeners.	    */
+    unsigned		 transmitter_cnt;/**<Number of transmitters.	    */
+
+    /* Shortcut for port info. */
+    unsigned		 clock_rate;	/**< Port's clock rate.		    */
+    unsigned		 samples_per_frame; /**< Port's samples per frame.  */
+    unsigned		 channel_count;	/**< Port's channel count.	    */
+
+    /* Calculated signal levels: */
+    unsigned		 tx_level;	/**< Last tx level to this port.    */
+    unsigned		 rx_level;	/**< Last rx level from this port.  */
+
+    /* The normalized signal level adjustment.
+     * A value of 128 (NORMAL_LEVEL) means there's no adjustment.
+     */
+    unsigned		 tx_adj_level;	/**< Adjustment for TX.		    */
+    unsigned		 rx_adj_level;	/**< Adjustment for RX.		    */
+
+    pj_timestamp	 ts_clock;
+    pj_timestamp	 ts_rx;
+
+    /* Tx buffer is a temporary buffer to be used when there's mismatch 
+     * between port's ptime with conference's ptime. This buffer is used as 
+     * the source to buffer the samples until there are enough samples to 
+     * fulfill a complete frame to be transmitted to the port.
+     */
+    pj_uint8_t		 tx_buf[BUFFER_SIZE]; /**< Tx buffer.		    */
+
+    /* When the port is not receiving signal from any other ports (e.g. when
+     * no other ports is transmitting to this port), the bridge periodically
+     * transmit NULL frame to the port to keep the port "alive" (for example,
+     * a stream port needs this heart-beat to periodically transmit silence
+     * frame to keep NAT binding alive).
+     *
+     * This NULL frame should be sent to the port at the port's ptime rate.
+     * So if the port's ptime is greater than the bridge's ptime, the bridge
+     * needs to delay the NULL frame until it's the right time to do so.
+     *
+     * This variable keeps track of how many pending NULL samples are being
+     * "held" for this port. Once this value reaches samples_per_frame
+     * value of the port, a NULL frame is sent. The samples value on this
+     * variable is clocked at the port's clock rate.
+     */
+    unsigned		 tx_heart_beat;
+};
+
+
+/*
+ * Conference bridge.
+ */
+struct pjmedia_conf
+{
+    unsigned		  options;	/**< Bitmask options.		    */
+    unsigned		  max_ports;	/**< Maximum ports.		    */
+    unsigned		  port_cnt;	/**< Current number of ports.	    */
+    unsigned		  connect_cnt;	/**< Total number of connections    */
+    pjmedia_snd_port	 *snd_dev_port;	/**< Sound device port.		    */
+    pjmedia_port	 *master_port;	/**< Port zero's port.		    */
+    char		  master_name_buf[80]; /**< Port0 name buffer.	    */
+    pj_mutex_t		 *mutex;	/**< Conference mutex.		    */
+    struct conf_port	**ports;	/**< Array of ports.		    */
+    unsigned		  clock_rate;	/**< Sampling rate.		    */
+    unsigned		  channel_count;/**< Number of channels (1=mono).   */
+    unsigned		  samples_per_frame;	/**< Samples per frame.	    */
+    unsigned		  bits_per_sample;	/**< Bits per sample.	    */
+    pj_uint8_t		  buf[BUFFER_SIZE];	/**< Common buffer.	    */
+};
+
+
+/* Prototypes */
+static pj_status_t put_frame(pjmedia_port *this_port, 
+			     const pjmedia_frame *frame);
+static pj_status_t get_frame(pjmedia_port *this_port, 
+			     pjmedia_frame *frame);
+static pj_status_t destroy_port(pjmedia_port *this_port);
+
+
+/*
+ * Create port.
+ */
+static pj_status_t create_conf_port( pj_pool_t *pool,
+				     pjmedia_conf *conf,
+				     pjmedia_port *port,
+				     const pj_str_t *name,
+				     struct conf_port **p_conf_port)
+{
+    struct conf_port *conf_port;
+    pjmedia_frame *f;
+
+    /* Create port. */
+    conf_port = PJ_POOL_ZALLOC_T(pool, struct conf_port);
+
+    /* Set name */
+    pj_strdup_with_null(pool, &conf_port->name, name);
+
+    /* Default has tx and rx enabled. */
+    conf_port->rx_setting = PJMEDIA_PORT_ENABLE;
+    conf_port->tx_setting = PJMEDIA_PORT_ENABLE;
+
+    /* Create transmit flag array */
+    conf_port->listener_slots = (SLOT_TYPE*)
+				pj_pool_zalloc(pool, 
+					  conf->max_ports * sizeof(SLOT_TYPE));
+    PJ_ASSERT_RETURN(conf_port->listener_slots, PJ_ENOMEM);
+
+    /* Save some port's infos, for convenience. */
+    if (port) {
+	conf_port->port = port;
+	conf_port->clock_rate = port->info.clock_rate;
+	conf_port->samples_per_frame = port->info.samples_per_frame;
+	conf_port->channel_count = port->info.channel_count;
+    } else {
+	conf_port->port = NULL;
+	conf_port->clock_rate = conf->clock_rate;
+	conf_port->samples_per_frame = conf->samples_per_frame;
+	conf_port->channel_count = conf->channel_count;
+    }
+
+    /* Init pjmedia_frame structure in the TX buffer. */
+    f = (pjmedia_frame*)conf_port->tx_buf;
+    f->buf = conf_port->tx_buf + sizeof(pjmedia_frame);
+    f->size = 0;
+
+    /* Done */
+    *p_conf_port = conf_port;
+    return PJ_SUCCESS;
+}
+
+/*
+ * Create port zero for the sound device.
+ */
+static pj_status_t create_sound_port( pj_pool_t *pool,
+				      pjmedia_conf *conf )
+{
+    struct conf_port *conf_port;
+    pj_str_t name = { "Master/sound", 12 };
+    pj_status_t status;
+
+
+    status = create_conf_port(pool, conf, NULL, &name, &conf_port);
+    if (status != PJ_SUCCESS)
+	return status;
+
+
+    /* Create sound device port: */
+
+    if ((conf->options & PJMEDIA_CONF_NO_DEVICE) == 0) {
+	pjmedia_snd_stream *strm;
+	pjmedia_snd_stream_info si;
+
+	/*
+	 * If capture is disabled then create player only port.
+	 * Otherwise create bidirectional sound device port.
+	 */
+	if (conf->options & PJMEDIA_CONF_NO_MIC)  {
+	    status = pjmedia_snd_port_create_player(pool, -1, conf->clock_rate,
+						    conf->channel_count,
+						    conf->samples_per_frame,
+						    conf->bits_per_sample, 
+						    0,	/* options */
+						    &conf->snd_dev_port);
+
+	} else {
+	    status = pjmedia_snd_port_create( pool, -1, -1, conf->clock_rate, 
+					      conf->channel_count, 
+					      conf->samples_per_frame,
+					      conf->bits_per_sample,
+					      0,    /* Options */
+					      &conf->snd_dev_port);
+
+	}
+
+	if (status != PJ_SUCCESS)
+	    return status;
+
+	strm = pjmedia_snd_port_get_snd_stream(conf->snd_dev_port);
+	status = pjmedia_snd_stream_get_info(strm, &si);
+	if (status == PJ_SUCCESS) {
+	    const pjmedia_snd_dev_info *snd_dev_info;
+	    if (conf->options & PJMEDIA_CONF_NO_MIC)
+		snd_dev_info = pjmedia_snd_get_dev_info(si.play_id);
+	    else
+		snd_dev_info = pjmedia_snd_get_dev_info(si.rec_id);
+	    pj_strdup2_with_null(pool, &conf_port->name, snd_dev_info->name);
+	}
+    }
+
+
+     /* Add the port to the bridge */
+    conf->ports[0] = conf_port;
+    conf->port_cnt++;
+
+
+    PJ_LOG(5,(THIS_FILE, "Sound device successfully created for port 0"));
+    return PJ_SUCCESS;
+}
+
+/*
+ * Create conference bridge.
+ */
+PJ_DEF(pj_status_t) pjmedia_conf_create( pj_pool_t *pool,
+					 unsigned max_ports,
+					 unsigned clock_rate,
+					 unsigned channel_count,
+					 unsigned samples_per_frame,
+					 unsigned bits_per_sample,
+					 unsigned options,
+					 pjmedia_conf **p_conf )
+{
+    pjmedia_conf *conf;
+    const pj_str_t name = { "Conf", 4 };
+    pj_status_t status;
+
+    /* Can only accept 16bits per sample, for now.. */
+    PJ_ASSERT_RETURN(bits_per_sample == 16, PJ_EINVAL);
+
+    PJ_LOG(5,(THIS_FILE, "Creating conference bridge with %d ports",
+	      max_ports));
+
+    /* Create and init conf structure. */
+    conf = PJ_POOL_ZALLOC_T(pool, pjmedia_conf);
+    PJ_ASSERT_RETURN(conf, PJ_ENOMEM);
+
+    conf->ports = (struct conf_port**) 
+		  pj_pool_zalloc(pool, max_ports*sizeof(void*));
+    PJ_ASSERT_RETURN(conf->ports, PJ_ENOMEM);
+
+    conf->options = options;
+    conf->max_ports = max_ports;
+    conf->clock_rate = clock_rate;
+    conf->channel_count = channel_count;
+    conf->samples_per_frame = samples_per_frame;
+    conf->bits_per_sample = bits_per_sample;
+
+    
+    /* Create and initialize the master port interface. */
+    conf->master_port = PJ_POOL_ZALLOC_T(pool, pjmedia_port);
+    PJ_ASSERT_RETURN(conf->master_port, PJ_ENOMEM);
+    
+    pjmedia_port_info_init(&conf->master_port->info, &name, SIGNATURE,
+			   clock_rate, channel_count, bits_per_sample,
+			   samples_per_frame);
+
+    conf->master_port->port_data.pdata = conf;
+    conf->master_port->port_data.ldata = 0;
+
+    conf->master_port->get_frame = &get_frame;
+    conf->master_port->put_frame = &put_frame;
+    conf->master_port->on_destroy = &destroy_port;
+
+
+    /* Create port zero for sound device. */
+    status = create_sound_port(pool, conf);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    /* Create mutex. */
+    status = pj_mutex_create_recursive(pool, "conf", &conf->mutex);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    /* If sound device was created, connect sound device to the
+     * master port.
+     */
+    if (conf->snd_dev_port) {
+	status = pjmedia_snd_port_connect( conf->snd_dev_port, 
+					   conf->master_port );
+	if (status != PJ_SUCCESS) {
+	    pjmedia_conf_destroy(conf);
+	    return status;
+	}
+    }
+
+    /* Done */
+
+    *p_conf = conf;
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Pause sound device.
+ */
+static pj_status_t pause_sound( pjmedia_conf *conf )
+{
+    /* Do nothing. */
+    PJ_UNUSED_ARG(conf);
+    return PJ_SUCCESS;
+}
+
+/*
+ * Resume sound device.
+ */
+static pj_status_t resume_sound( pjmedia_conf *conf )
+{
+    /* Do nothing. */
+    PJ_UNUSED_ARG(conf);
+    return PJ_SUCCESS;
+}
+
+
+/**
+ * Destroy conference bridge.
+ */
+PJ_DEF(pj_status_t) pjmedia_conf_destroy( pjmedia_conf *conf )
+{
+    PJ_ASSERT_RETURN(conf != NULL, PJ_EINVAL);
+
+    /* Destroy sound device port. */
+    if (conf->snd_dev_port) {
+	pjmedia_snd_port_destroy(conf->snd_dev_port);
+	conf->snd_dev_port = NULL;
+    }
+
+    /* Destroy mutex */
+    pj_mutex_destroy(conf->mutex);
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Destroy the master port (will destroy the conference)
+ */
+static pj_status_t destroy_port(pjmedia_port *this_port)
+{
+    pjmedia_conf *conf = (pjmedia_conf*) this_port->port_data.pdata;
+    return pjmedia_conf_destroy(conf);
+}
+
+/*
+ * Get port zero interface.
+ */
+PJ_DEF(pjmedia_port*) pjmedia_conf_get_master_port(pjmedia_conf *conf)
+{
+    /* Sanity check. */
+    PJ_ASSERT_RETURN(conf != NULL, NULL);
+
+    /* Can only return port interface when PJMEDIA_CONF_NO_DEVICE was
+     * present in the option.
+     */
+    PJ_ASSERT_RETURN((conf->options & PJMEDIA_CONF_NO_DEVICE) != 0, NULL);
+    
+    return conf->master_port;
+}
+
+
+/*
+ * Set master port name.
+ */
+PJ_DEF(pj_status_t) pjmedia_conf_set_port0_name(pjmedia_conf *conf,
+						const pj_str_t *name)
+{
+    unsigned len;
+
+    /* Sanity check. */
+    PJ_ASSERT_RETURN(conf != NULL && name != NULL, PJ_EINVAL);
+
+    len = name->slen;
+    if (len > sizeof(conf->master_name_buf))
+	len = sizeof(conf->master_name_buf);
+    
+    if (len > 0) pj_memcpy(conf->master_name_buf, name->ptr, len);
+
+    conf->ports[0]->name.ptr = conf->master_name_buf;
+    conf->ports[0]->name.slen = len;
+
+    if (conf->master_port)
+	conf->master_port->info.name = conf->ports[0]->name;
+
+    return PJ_SUCCESS;
+}
+
+/*
+ * Add stream port to the conference bridge.
+ */
+PJ_DEF(pj_status_t) pjmedia_conf_add_port( pjmedia_conf *conf,
+					   pj_pool_t *pool,
+					   pjmedia_port *strm_port,
+					   const pj_str_t *port_name,
+					   unsigned *p_port )
+{
+    struct conf_port *conf_port;
+    unsigned index;
+    pj_status_t status;
+
+    PJ_ASSERT_RETURN(conf && pool && strm_port, PJ_EINVAL);
+    PJ_ASSERT_RETURN(conf->clock_rate == strm_port->info.clock_rate, 
+		     PJMEDIA_ENCCLOCKRATE);
+    PJ_ASSERT_RETURN(conf->channel_count == strm_port->info.channel_count, 
+		     PJMEDIA_ENCCHANNEL);
+    PJ_ASSERT_RETURN(conf->bits_per_sample == strm_port->info.bits_per_sample,
+		     PJMEDIA_ENCBITS);
+    PJ_ASSERT_RETURN((conf->samples_per_frame %
+		     strm_port->info.samples_per_frame==0) ||
+		     (strm_port->info.samples_per_frame %
+		     conf->samples_per_frame==0),
+		     PJMEDIA_ENCSAMPLESPFRAME);
+
+    /* If port_name is not specified, use the port's name */
+    if (!port_name)
+	port_name = &strm_port->info.name;
+
+    pj_mutex_lock(conf->mutex);
+
+    if (conf->port_cnt >= conf->max_ports) {
+	pj_assert(!"Too many ports");
+	pj_mutex_unlock(conf->mutex);
+	return PJ_ETOOMANY;
+    }
+
+    /* Find empty port in the conference bridge. */
+    for (index=0; index < conf->max_ports; ++index) {
+	if (conf->ports[index] == NULL)
+	    break;
+    }
+
+    pj_assert(index != conf->max_ports);
+
+    /* Create conf port structure. */
+    status = create_conf_port(pool, conf, strm_port, port_name, &conf_port);
+    if (status != PJ_SUCCESS) {
+	pj_mutex_unlock(conf->mutex);
+	return status;
+    }
+
+    /* Put the port. */
+    conf->ports[index] = conf_port;
+    conf->port_cnt++;
+
+    /* Done. */
+    if (p_port) {
+	*p_port = index;
+    }
+
+    pj_mutex_unlock(conf->mutex);
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Add passive port.
+ */
+PJ_DEF(pj_status_t) pjmedia_conf_add_passive_port( pjmedia_conf *conf,
+						   pj_pool_t *pool,
+						   const pj_str_t *name,
+						   unsigned clock_rate,
+						   unsigned channel_count,
+						   unsigned samples_per_frame,
+						   unsigned bits_per_sample,
+						   unsigned options,
+						   unsigned *p_slot,
+						   pjmedia_port **p_port )
+{
+    PJ_UNUSED_ARG(conf);
+    PJ_UNUSED_ARG(pool);
+    PJ_UNUSED_ARG(name);
+    PJ_UNUSED_ARG(clock_rate);
+    PJ_UNUSED_ARG(channel_count);
+    PJ_UNUSED_ARG(samples_per_frame);
+    PJ_UNUSED_ARG(bits_per_sample);
+    PJ_UNUSED_ARG(options);
+    PJ_UNUSED_ARG(p_slot);
+    PJ_UNUSED_ARG(p_port);
+
+    return PJ_ENOTSUP;
+}
+
+
+
+/*
+ * Change TX and RX settings for the port.
+ */
+PJ_DEF(pj_status_t) pjmedia_conf_configure_port( pjmedia_conf *conf,
+						  unsigned slot,
+						  pjmedia_port_op tx,
+						  pjmedia_port_op rx)
+{
+    struct conf_port *conf_port;
+
+    /* Check arguments */
+    PJ_ASSERT_RETURN(conf && slot<conf->max_ports, PJ_EINVAL);
+
+    /* Port must be valid. */
+    PJ_ASSERT_RETURN(conf->ports[slot] != NULL, PJ_EINVAL);
+
+    conf_port = conf->ports[slot];
+
+    if (tx != PJMEDIA_PORT_NO_CHANGE)
+	conf_port->tx_setting = tx;
+
+    if (rx != PJMEDIA_PORT_NO_CHANGE)
+	conf_port->rx_setting = rx;
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Connect port.
+ */
+PJ_DEF(pj_status_t) pjmedia_conf_connect_port( pjmedia_conf *conf,
+					       unsigned src_slot,
+					       unsigned sink_slot,
+					       int level )
+{
+    struct conf_port *src_port, *dst_port;
+    pj_bool_t start_sound = PJ_FALSE;
+    unsigned i;
+
+    /* Check arguments */
+    PJ_ASSERT_RETURN(conf && src_slot<conf->max_ports && 
+		     sink_slot<conf->max_ports, PJ_EINVAL);
+
+    /* Ports must be valid. */
+    PJ_ASSERT_RETURN(conf->ports[src_slot] != NULL, PJ_EINVAL);
+    PJ_ASSERT_RETURN(conf->ports[sink_slot] != NULL, PJ_EINVAL);
+
+    /* For now, level MUST be zero. */
+    PJ_ASSERT_RETURN(level == 0, PJ_EINVAL);
+
+    pj_mutex_lock(conf->mutex);
+
+    src_port = conf->ports[src_slot];
+    dst_port = conf->ports[sink_slot];
+
+    /* Check if source and sink has compatible format */
+    if (src_slot != 0 && sink_slot != 0 && 
+	src_port->port->info.format.u32 != dst_port->port->info.format.u32)
+    {
+	pj_mutex_unlock(conf->mutex);
+	return PJMEDIA_ENOTCOMPATIBLE;
+    }
+
+    /* Check if sink is listening to other ports */
+    if (dst_port->transmitter_cnt > 0) {
+	pj_mutex_unlock(conf->mutex);
+	return PJ_ETOOMANYCONN;
+    }
+
+    /* Check if connection has been made */
+    for (i=0; i<src_port->listener_cnt; ++i) {
+	if (src_port->listener_slots[i] == sink_slot)
+	    break;
+    }
+
+    if (i == src_port->listener_cnt) {
+	src_port->listener_slots[src_port->listener_cnt] = sink_slot;
+	++conf->connect_cnt;
+	++src_port->listener_cnt;
+	++dst_port->transmitter_cnt;
+
+	if (conf->connect_cnt == 1)
+	    start_sound = 1;
+
+	PJ_LOG(4,(THIS_FILE,"Port %d (%.*s) transmitting to port %d (%.*s)",
+		  src_slot,
+		  (int)src_port->name.slen,
+		  src_port->name.ptr,
+		  sink_slot,
+		  (int)dst_port->name.slen,
+		  dst_port->name.ptr));
+    }
+
+    pj_mutex_unlock(conf->mutex);
+
+    /* Sound device must be started without mutex, otherwise the
+     * sound thread will deadlock (?)
+     */
+    if (start_sound)
+	resume_sound(conf);
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Disconnect port
+ */
+PJ_DEF(pj_status_t) pjmedia_conf_disconnect_port( pjmedia_conf *conf,
+						  unsigned src_slot,
+						  unsigned sink_slot )
+{
+    struct conf_port *src_port, *dst_port;
+    unsigned i;
+
+    /* Check arguments */
+    PJ_ASSERT_RETURN(conf && src_slot<conf->max_ports && 
+		     sink_slot<conf->max_ports, PJ_EINVAL);
+
+    /* Ports must be valid. */
+    PJ_ASSERT_RETURN(conf->ports[src_slot] != NULL, PJ_EINVAL);
+    PJ_ASSERT_RETURN(conf->ports[sink_slot] != NULL, PJ_EINVAL);
+
+    pj_mutex_lock(conf->mutex);
+
+    src_port = conf->ports[src_slot];
+    dst_port = conf->ports[sink_slot];
+
+    /* Check if connection has been made */
+    for (i=0; i<src_port->listener_cnt; ++i) {
+	if (src_port->listener_slots[i] == sink_slot)
+	    break;
+    }
+
+    if (i != src_port->listener_cnt) {
+	pj_assert(src_port->listener_cnt > 0 && 
+		  src_port->listener_cnt < conf->max_ports);
+	pj_assert(dst_port->transmitter_cnt > 0 && 
+		  dst_port->transmitter_cnt < conf->max_ports);
+	pj_array_erase(src_port->listener_slots, sizeof(SLOT_TYPE), 
+		       src_port->listener_cnt, i);
+	--conf->connect_cnt;
+	--src_port->listener_cnt;
+	--dst_port->transmitter_cnt;
+
+	PJ_LOG(4,(THIS_FILE,
+		  "Port %d (%.*s) stop transmitting to port %d (%.*s)",
+		  src_slot,
+		  (int)src_port->name.slen,
+		  src_port->name.ptr,
+		  sink_slot,
+		  (int)dst_port->name.slen,
+		  dst_port->name.ptr));
+    }
+
+    pj_mutex_unlock(conf->mutex);
+
+    if (conf->connect_cnt == 0) {
+	pause_sound(conf);
+    }
+
+    return PJ_SUCCESS;
+}
+
+/*
+ * Get number of ports currently registered to the conference bridge.
+ */
+PJ_DEF(unsigned) pjmedia_conf_get_port_count(pjmedia_conf *conf)
+{
+    return conf->port_cnt;
+}
+
+/*
+ * Get total number of ports connections currently set up in the bridge.
+ */
+PJ_DEF(unsigned) pjmedia_conf_get_connect_count(pjmedia_conf *conf)
+{
+    return conf->connect_cnt;
+}
+
+
+/*
+ * Remove the specified port.
+ */
+PJ_DEF(pj_status_t) pjmedia_conf_remove_port( pjmedia_conf *conf,
+					      unsigned port )
+{
+    struct conf_port *conf_port;
+    unsigned i;
+
+    /* Check arguments */
+    PJ_ASSERT_RETURN(conf && port < conf->max_ports, PJ_EINVAL);
+
+    /* Port must be valid. */
+    PJ_ASSERT_RETURN(conf->ports[port] != NULL, PJ_EINVAL);
+
+    /* Suspend the sound devices.
+     * Don't want to remove port while port is being accessed by sound
+     * device's threads!
+     */
+
+    pj_mutex_lock(conf->mutex);
+
+    conf_port = conf->ports[port];
+    conf_port->tx_setting = PJMEDIA_PORT_DISABLE;
+    conf_port->rx_setting = PJMEDIA_PORT_DISABLE;
+
+    /* Remove this port from transmit array of other ports. */
+    for (i=0; i<conf->max_ports; ++i) {
+	unsigned j;
+	struct conf_port *src_port;
+
+	src_port = conf->ports[i];
+
+	if (!src_port)
+	    continue;
+
+	if (src_port->listener_cnt == 0)
+	    continue;
+
+	for (j=0; j<src_port->listener_cnt; ++j) {
+	    if (src_port->listener_slots[j] == port) {
+		pj_array_erase(src_port->listener_slots, sizeof(SLOT_TYPE),
+			       src_port->listener_cnt, j);
+		pj_assert(conf->connect_cnt > 0);
+		--conf->connect_cnt;
+		--src_port->listener_cnt;
+		break;
+	    }
+	}
+    }
+
+    /* Update transmitter_cnt of ports we're transmitting to */
+    while (conf_port->listener_cnt) {
+	unsigned dst_slot;
+	struct conf_port *dst_port;
+
+	dst_slot = conf_port->listener_slots[conf_port->listener_cnt-1];
+	dst_port = conf->ports[dst_slot];
+	--dst_port->transmitter_cnt;
+	--conf_port->listener_cnt;
+	pj_assert(conf->connect_cnt > 0);
+	--conf->connect_cnt;
+    }
+
+    /* Remove the port. */
+    conf->ports[port] = NULL;
+    --conf->port_cnt;
+
+    pj_mutex_unlock(conf->mutex);
+
+
+    /* Stop sound if there's no connection. */
+    if (conf->connect_cnt == 0) {
+	pause_sound(conf);
+    }
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Enum ports.
+ */
+PJ_DEF(pj_status_t) pjmedia_conf_enum_ports( pjmedia_conf *conf,
+					     unsigned ports[],
+					     unsigned *p_count )
+{
+    unsigned i, count=0;
+
+    PJ_ASSERT_RETURN(conf && p_count && ports, PJ_EINVAL);
+
+    for (i=0; i<conf->max_ports && count<*p_count; ++i) {
+	if (!conf->ports[i])
+	    continue;
+
+	ports[count++] = i;
+    }
+
+    *p_count = count;
+    return PJ_SUCCESS;
+}
+
+/*
+ * Get port info
+ */
+PJ_DEF(pj_status_t) pjmedia_conf_get_port_info( pjmedia_conf *conf,
+						unsigned slot,
+						pjmedia_conf_port_info *info)
+{
+    struct conf_port *conf_port;
+
+    /* Check arguments */
+    PJ_ASSERT_RETURN(conf && slot<conf->max_ports, PJ_EINVAL);
+
+    /* Port must be valid. */
+    PJ_ASSERT_RETURN(conf->ports[slot] != NULL, PJ_EINVAL);
+
+    conf_port = conf->ports[slot];
+
+    pj_bzero(info, sizeof(pjmedia_conf_port_info));
+
+    info->slot = slot;
+    info->name = conf_port->name;
+    info->tx_setting = conf_port->tx_setting;
+    info->rx_setting = conf_port->rx_setting;
+    info->listener_cnt = conf_port->listener_cnt;
+    info->listener_slots = conf_port->listener_slots;
+    info->clock_rate = conf_port->clock_rate;
+    info->channel_count = conf_port->channel_count;
+    info->samples_per_frame = conf_port->samples_per_frame;
+    info->bits_per_sample = conf->bits_per_sample;
+    info->format = slot? conf_port->port->info.format : 
+			 conf->master_port->info.format;
+
+    return PJ_SUCCESS;
+}
+
+
+PJ_DEF(pj_status_t) pjmedia_conf_get_ports_info(pjmedia_conf *conf,
+						unsigned *size,
+						pjmedia_conf_port_info info[])
+{
+    unsigned i, count=0;
+
+    PJ_ASSERT_RETURN(conf && size && info, PJ_EINVAL);
+
+    for (i=0; i<conf->max_ports && count<*size; ++i) {
+	if (!conf->ports[i])
+	    continue;
+
+	pjmedia_conf_get_port_info(conf, i, &info[count]);
+	++count;
+    }
+
+    *size = count;
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Get signal level.
+ */
+PJ_DEF(pj_status_t) pjmedia_conf_get_signal_level( pjmedia_conf *conf,
+						   unsigned slot,
+						   unsigned *tx_level,
+						   unsigned *rx_level)
+{
+    struct conf_port *conf_port;
+
+    /* Check arguments */
+    PJ_ASSERT_RETURN(conf && slot<conf->max_ports, PJ_EINVAL);
+
+    /* Port must be valid. */
+    PJ_ASSERT_RETURN(conf->ports[slot] != NULL, PJ_EINVAL);
+
+    conf_port = conf->ports[slot];
+
+    if (tx_level != NULL) {
+	*tx_level = conf_port->tx_level;
+    }
+
+    if (rx_level != NULL) 
+	*rx_level = conf_port->rx_level;
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Adjust RX level of individual port.
+ */
+PJ_DEF(pj_status_t) pjmedia_conf_adjust_rx_level( pjmedia_conf *conf,
+						  unsigned slot,
+						  int adj_level )
+{
+    struct conf_port *conf_port;
+
+    /* Check arguments */
+    PJ_ASSERT_RETURN(conf && slot<conf->max_ports, PJ_EINVAL);
+
+    /* Port must be valid. */
+    PJ_ASSERT_RETURN(conf->ports[slot] != NULL, PJ_EINVAL);
+
+    /* Value must be from -128 to +127 */
+    /* Disabled, you can put more than +127, at your own risk: 
+     PJ_ASSERT_RETURN(adj_level >= -128 && adj_level <= 127, PJ_EINVAL);
+     */
+    PJ_ASSERT_RETURN(adj_level >= -128, PJ_EINVAL);
+
+    conf_port = conf->ports[slot];
+
+    /* Set normalized adjustment level. */
+    conf_port->rx_adj_level = adj_level + NORMAL_LEVEL;
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Adjust TX level of individual port.
+ */
+PJ_DEF(pj_status_t) pjmedia_conf_adjust_tx_level( pjmedia_conf *conf,
+						  unsigned slot,
+						  int adj_level )
+{
+    struct conf_port *conf_port;
+
+    /* Check arguments */
+    PJ_ASSERT_RETURN(conf && slot<conf->max_ports, PJ_EINVAL);
+
+    /* Port must be valid. */
+    PJ_ASSERT_RETURN(conf->ports[slot] != NULL, PJ_EINVAL);
+
+    /* Value must be from -128 to +127 */
+    /* Disabled, you can put more than +127,, at your own risk:
+     PJ_ASSERT_RETURN(adj_level >= -128 && adj_level <= 127, PJ_EINVAL);
+     */
+    PJ_ASSERT_RETURN(adj_level >= -128, PJ_EINVAL);
+
+    conf_port = conf->ports[slot];
+
+    return PJ_SUCCESS;
+}
+
+/* Deliver frm_src to a conference port (via frm_dst), eventually call 
+ * port's put_frame() when samples count in the frm_dst are equal to 
+ * port's samples_per_frame.
+ */
+static pj_status_t deliver_frame(struct conf_port *cport_dst,
+			         pjmedia_frame *frm_dst,
+			         const pjmedia_frame *frm_src)
+{
+    PJ_TODO(MAKE_SURE_DEST_FRAME_HAS_ENOUGH_SPACE);
+
+    if (frm_src->type == PJMEDIA_FRAME_TYPE_EXTENDED) {
+	pjmedia_frame_ext *f_src = (pjmedia_frame_ext*)frm_src;
+	pjmedia_frame_ext *f_dst = (pjmedia_frame_ext*)frm_dst;
+	unsigned i;
+
+	/* Copy frame to listener's TX buffer. */
+	for (i = 0; i < f_src->subframe_cnt; ++i) {
+	    pjmedia_frame_ext_subframe *sf;
+	    
+	    sf = pjmedia_frame_ext_get_subframe(f_src, i);
+	    pjmedia_frame_ext_append_subframe(f_dst, sf->data, sf->bitlen, 
+					      f_src->samples_cnt / 
+					      f_src->subframe_cnt);
+
+	    /* Check if it's time to deliver the TX buffer to listener, 
+	     * i.e: samples count in TX buffer equal to listener's
+	     * samples per frame.
+	     */
+	    if (f_dst->samples_cnt == cport_dst->samples_per_frame)
+	    {
+		f_dst->base.type = PJMEDIA_FRAME_TYPE_EXTENDED;
+		if (cport_dst->port) {
+		    pjmedia_port_put_frame(cport_dst->port, (pjmedia_frame*)f_dst);
+
+		    /* Reset TX buffer. */
+		    f_dst->subframe_cnt = 0;
+		    f_dst->samples_cnt = 0;
+		}
+	    }
+	}
+
+    } else {
+
+	pjmedia_frame *f_dst = (pjmedia_frame*)frm_dst;
+	pj_int16_t *f_start, *f_end;
+
+	f_start = (pj_int16_t*)frm_src->buf;
+	f_end   = f_start + (frm_src->size >> 1);
+	while (f_start < f_end) {
+	    unsigned nsamples_to_copy, nsamples_req;
+
+	    nsamples_to_copy = f_end - f_start;
+	    nsamples_req = cport_dst->samples_per_frame - (f_dst->size >> 1);
+	    if (nsamples_to_copy > nsamples_req)
+		nsamples_to_copy = nsamples_req;
+	    pjmedia_copy_samples((pj_int16_t*)f_dst->buf + (f_dst->size >> 1), 
+				 f_start, 
+				 nsamples_to_copy);
+	    f_dst->size += nsamples_to_copy << 1;
+	    f_start += nsamples_to_copy;
+
+	    /* Check if it's time to deliver the TX buffer to listener, 
+	     * i.e: samples count in TX buffer equal to listener's
+	     * samples per frame.
+	     */
+	    if ((f_dst->size >> 1) == cport_dst->samples_per_frame)
+	    {
+		f_dst->type = PJMEDIA_FRAME_TYPE_AUDIO;
+		if (cport_dst->port) {
+		    pjmedia_port_put_frame(cport_dst->port, f_dst);
+		 
+		    /* Reset TX buffer. */
+		    f_dst->size = 0;
+		}
+	    }
+	}
+
+    }
+
+    return PJ_SUCCESS;
+}
+
+/*
+ * Player callback.
+ */
+static pj_status_t get_frame(pjmedia_port *this_port, 
+			     pjmedia_frame *frame)
+{
+    pjmedia_conf *conf = (pjmedia_conf*) this_port->port_data.pdata;
+    unsigned ci, i;
+    
+    PJ_TODO(ADJUST_AND_CALC_RX_TX_LEVEL_FOR_PCM_FRAMES);
+
+    TRACE_((THIS_FILE, "- clock -"));
+
+    /* Must lock mutex */
+    pj_mutex_lock(conf->mutex);
+
+    /* Call get_frame() from all ports (except port 0) that has 
+     * receiver and distribute the frame (put the frame to the destination 
+     * port's buffer to accommodate different ptime, and ultimately call 
+     * put_frame() of that port) to ports that are receiving from this port.
+     */
+    for (i=1, ci=1; i<conf->max_ports && ci<conf->port_cnt; ++i) {
+	struct conf_port *cport = conf->ports[i];
+
+	/* Skip empty port. */
+	if (!cport)
+	    continue;
+
+	/* Var "ci" is to count how many ports have been visited so far. */
+	++ci;
+
+	/* Skip if we're not allowed to receive from this port. */
+	if (cport->rx_setting == PJMEDIA_PORT_DISABLE) {
+	    cport->rx_level = 0;
+	    continue;
+	}
+
+	/* Also skip if this port doesn't have listeners. */
+	if (cport->listener_cnt == 0) {
+	    cport->rx_level = 0;
+	    continue;
+	}
+
+	pj_add_timestamp32(&cport->ts_clock, conf->samples_per_frame);
+
+	/* This loop will make sure the ptime between port & conf port 
+	 * are synchronized.
+	 */
+	while (pj_cmp_timestamp(&cport->ts_clock, &cport->ts_rx) > 0) {
+	    pjmedia_frame *f = (pjmedia_frame*) conf->buf;
+	    pj_status_t status;
+	    unsigned j;
+
+	    pj_add_timestamp32(&cport->ts_rx, cport->samples_per_frame);
+	    
+	    f->buf = &conf->buf[sizeof(pjmedia_frame)];
+	    f->size = BUFFER_SIZE - sizeof(pjmedia_frame);
+
+	    /* Get frame from port. */
+	    status = pjmedia_port_get_frame(cport->port, f);
+	    if (status != PJ_SUCCESS)
+		continue;
+
+	    if (f->type == PJMEDIA_FRAME_TYPE_NONE) {
+		if (cport->port->info.format.u32 == PJMEDIA_FOURCC_L16) {
+		    pjmedia_zero_samples((pj_int16_t*)f->buf,
+					 cport->samples_per_frame);
+		    f->size = cport->samples_per_frame << 1;
+		    f->type = PJMEDIA_FRAME_TYPE_AUDIO;
+		} else {
+		    /* Handle DTX */
+		    PJ_TODO(HANDLE_DTX);
+		}
+	    }
+
+	    /* Put the frame to all listeners. */
+	    for (j=0; j < cport->listener_cnt; ++j) 
+	    {
+		struct conf_port *listener;
+		pjmedia_frame *frm_dst;
+
+		listener = conf->ports[cport->listener_slots[j]];
+
+		/* Skip if this listener doesn't want to receive audio */
+		if (listener->tx_setting != PJMEDIA_PORT_ENABLE)
+		    continue;
+    	    
+		if (listener->port)
+		    frm_dst = frame;
+		else
+		    frm_dst = (pjmedia_frame*)listener->tx_buf;
+
+		status = deliver_frame(listener, frm_dst, f);
+		if (status != PJ_SUCCESS)
+		    continue;
+	    }
+	}
+	
+	/* Keep alive mechanism. */
+	PJ_TODO(SEND_KEEP_ALIVE_WHEN_NEEDED);
+    }
+
+    /* Unlock mutex */
+    pj_mutex_unlock(conf->mutex);
+
+    return PJ_SUCCESS;
+}
+
+/*
+ * Recorder callback.
+ */
+static pj_status_t put_frame(pjmedia_port *this_port, 
+			     const pjmedia_frame *frame)
+{
+    pjmedia_conf *conf = (pjmedia_conf*) this_port->port_data.pdata;
+    struct conf_port *port = conf->ports[this_port->port_data.ldata];
+    unsigned j;
+
+    /* Check for correct size. */
+    PJ_ASSERT_RETURN( frame->size == conf->samples_per_frame *
+				     conf->bits_per_sample / 8,
+		      PJMEDIA_ENCSAMPLESPFRAME);
+
+    /* Skip if this port is muted/disabled. */
+    if (port->rx_setting != PJMEDIA_PORT_ENABLE) {
+	return PJ_SUCCESS;
+    }
+
+    /* Skip if no port is listening to the microphone */
+    if (port->listener_cnt == 0) {
+	return PJ_SUCCESS;
+    }
+
+    if (frame->type == PJMEDIA_FRAME_TYPE_NONE) {
+	if (this_port->info.format.u32 == PJMEDIA_FOURCC_L16) {
+	    pjmedia_frame *f = (pjmedia_frame*)port->tx_buf;
+
+	    pjmedia_zero_samples((pj_int16_t*)f->buf,
+				 port->samples_per_frame);
+	    f->size = port->samples_per_frame << 1;
+	    f->type = PJMEDIA_FRAME_TYPE_AUDIO;
+	    frame = f;
+	} else {
+	    /* Handle DTX */
+	    PJ_TODO(HANDLE_DTX);
+	}
+    }
+
+    /* Put the frame to all listeners. */
+    for (j=0; j < port->listener_cnt; ++j) 
+    {
+	struct conf_port *listener;
+	pjmedia_frame *frm_dst;
+	pj_status_t status;
+
+	listener = conf->ports[port->listener_slots[j]];
+
+	/* Skip if this listener doesn't want to receive audio */
+	if (listener->tx_setting != PJMEDIA_PORT_ENABLE)
+	    continue;
+
+	/* Skip loopback for now. */
+	if (listener == port)
+	    continue;
+	    
+	frm_dst = (pjmedia_frame*)listener->tx_buf;
+
+	status = deliver_frame(listener, frm_dst, frame);
+	if (status != PJ_SUCCESS)
+	    continue;
+    }
+
+    return PJ_SUCCESS;
+}
+
+#endif
diff --git a/pjmedia/src/pjmedia/conference.c b/pjmedia/src/pjmedia/conference.c
index 9cae6a7..c71ef2e 100644
--- a/pjmedia/src/pjmedia/conference.c
+++ b/pjmedia/src/pjmedia/conference.c
@@ -33,6 +33,7 @@
 #include <pj/pool.h>
 #include <pj/string.h>
 
+#if !defined(PJMEDIA_CONF_USE_SWITCH_BOARD) || PJMEDIA_CONF_USE_SWITCH_BOARD==0
 
 /* CONF_DEBUG enables detailed operation of the conference bridge.
  * Beware that it prints large amounts of logs (several lines per frame).
@@ -1987,3 +1988,4 @@
     return status;
 }
 
+#endif
diff --git a/pjmedia/src/pjmedia/symbian_sound.cpp b/pjmedia/src/pjmedia/symbian_sound.cpp
index b7c92f9..e6b8651 100644
--- a/pjmedia/src/pjmedia/symbian_sound.cpp
+++ b/pjmedia/src/pjmedia/symbian_sound.cpp
@@ -23,6 +23,7 @@
 #include <pj/log.h>
 #include <pj/os.h>
 
+#if PJMEDIA_SOUND_IMPLEMENTATION == PJMEDIA_SOUND_SYMB_MDA_SOUND
 
 /*
  * This file provides sound implementation for Symbian Audio Streaming
@@ -942,3 +943,5 @@
     PJ_UNUSED_ARG(output_latency);
     return PJ_SUCCESS;
 }
+
+#endif
diff --git a/pjmedia/src/pjmedia/symbian_sound_aps.cpp b/pjmedia/src/pjmedia/symbian_sound_aps.cpp
index 2e9bf2f..687815c 100644
--- a/pjmedia/src/pjmedia/symbian_sound_aps.cpp
+++ b/pjmedia/src/pjmedia/symbian_sound_aps.cpp
@@ -17,6 +17,7 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
  */
+#include <pjmedia/symbian_sound_aps.h>
 #include <pjmedia/sound.h>
 #include <pjmedia/alaw_ulaw.h>
 #include <pjmedia/errno.h>
@@ -25,6 +26,8 @@
 #include <pj/math.h>
 #include <pj/os.h>
 
+#if PJMEDIA_SOUND_IMPLEMENTATION == PJMEDIA_SOUND_SYMB_APS_SOUND
+
 #include <e32msgqueue.h>
 #include <sounddevice.h>
 #include <APSClientSession.h>
@@ -45,7 +48,7 @@
 #   define TRACE_(st)
 #endif
 
-static pjmedia_snd_dev_info symbian_snd_dev_info = 
+static pjmedia_snd_dev_info symbian_snd_dev_info =
 {
     "Symbian Sound Device (APS)",
     1,
@@ -56,56 +59,81 @@
 /* App UID to open global APS queues to communicate with the APS server. */
 extern TPtrC		    APP_UID;
 
-/* Default setting for loudspeaker */
-static pj_bool_t act_loudspeaker = PJ_FALSE;
+/* Default setting */
+static pjmedia_snd_aps_setting def_setting;
+
+/* APS G.711 frame length */
+static pj_uint8_t aps_g711_frame_len;
+
+/* Pool factory */
+static pj_pool_factory *snd_pool_factory;
 
 /* Forward declaration of CPjAudioEngine */
 class CPjAudioEngine;
 
-/* 
- * PJMEDIA Sound Stream instance 
+/*
+ * PJMEDIA Sound Stream instance
  */
 struct pjmedia_snd_stream
 {
     // Pool
-    pj_pool_t			*pool;
+    pj_pool_t		*pool;
 
     // Common settings.
-    pjmedia_dir		 	 dir;
-    unsigned			 clock_rate;
-    unsigned			 channel_count;
-    unsigned			 samples_per_frame;
+    pjmedia_dir		 dir;
+    unsigned		 clock_rate;
+    unsigned		 channel_count;
+    unsigned		 samples_per_frame;
+    pjmedia_snd_rec_cb   rec_cb;
+    pjmedia_snd_play_cb	 play_cb;
+    void                *user_data;
 
     // Audio engine
-    CPjAudioEngine		*engine;
-};
+    CPjAudioEngine	*engine;
 
-static pj_pool_factory *snd_pool_factory;
+    pj_timestamp  	 ts_play;
+    pj_timestamp	 ts_rec;
+
+    pj_int16_t		*play_buf;
+    pj_uint16_t		 play_buf_len;
+    pj_uint16_t		 play_buf_start;
+    pj_int16_t		*rec_buf;
+    pj_uint16_t		 rec_buf_len;
+};
 
 
 /*
  * Utility: print sound device error
  */
-static void snd_perror(const char *title, TInt rc) 
+static void snd_perror(const char *title, TInt rc)
 {
     PJ_LOG(1,(THIS_FILE, "%s (error code=%d)", title, rc));
 }
- 
+
 //////////////////////////////////////////////////////////////////////////////
 //
 
+typedef void(*PjAudioCallback)(TAPSCommBuffer &buf, void *user_data);
+
 /**
  * Abstract class for handler of callbacks from APS client.
  */
 class MQueueHandlerObserver
 {
 public:
+    MQueueHandlerObserver(PjAudioCallback RecCb_, PjAudioCallback PlayCb_,
+			  void *UserData_)
+    : RecCb(RecCb_), PlayCb(PlayCb_), UserData(UserData_)
+    {}
+
     virtual void InputStreamInitialized(const TInt aStatus) = 0;
     virtual void OutputStreamInitialized(const TInt aStatus) = 0;
     virtual void NotifyError(const TInt aError) = 0;
 
-    virtual void RecCb(TAPSCommBuffer &buffer) = 0;
-    virtual void PlayCb(TAPSCommBuffer &buffer) = 0;
+public:
+    PjAudioCallback RecCb;
+    PjAudioCallback PlayCb;
+    void *UserData;
 };
 
 /**
@@ -132,11 +160,13 @@
     	EAPSRecorderInitComplete    = 6
     };
 
-    static CQueueHandler* NewL(MQueueHandlerObserver* aObserver, 
-			       RMsgQueue<TAPSCommBuffer>* aQ, 
+    static CQueueHandler* NewL(MQueueHandlerObserver* aObserver,
+			       RMsgQueue<TAPSCommBuffer>* aQ,
+			       RMsgQueue<TAPSCommBuffer>* aWriteQ,
 			       TQueueHandlerType aType)
     {
-	CQueueHandler* self = new (ELeave) CQueueHandler(aObserver, aQ, aType);
+	CQueueHandler* self = new (ELeave) CQueueHandler(aObserver, aQ, aWriteQ,
+							 aType);
 	CleanupStack::PushL(self);
 	self->ConstructL();
 	CleanupStack::Pop(self);
@@ -154,11 +184,12 @@
 
 private:
     // Constructor
-    CQueueHandler(MQueueHandlerObserver* aObserver, 
-		  RMsgQueue<TAPSCommBuffer>* aQ, 
-		  TQueueHandlerType aType) 
+    CQueueHandler(MQueueHandlerObserver* aObserver,
+		  RMsgQueue<TAPSCommBuffer>* aQ,
+		  RMsgQueue<TAPSCommBuffer>* aWriteQ,
+		  TQueueHandlerType aType)
 	: CActive(CActive::EPriorityHigh),
-	  iQ(aQ), iObserver(aObserver), iType(aType)
+	  iQ(aQ), iWriteQ(aWriteQ), iObserver(aObserver), iType(aType)
     {
 	CActiveScheduler::Add(this);
 
@@ -177,7 +208,7 @@
 	if (iStatus != KErrNone) {
 	    iObserver->NotifyError(iStatus.Int());
 	    return;
-        }    
+        }
 
 	TAPSCommBuffer buffer;
 	TInt ret = iQ->Receive(buffer);
@@ -190,7 +221,9 @@
 	switch (iType) {
 	case ERecordQueue:
 	    if (buffer.iCommand == EAPSRecordData) {
-		iObserver->RecCb(buffer);
+		iObserver->RecCb(buffer, iObserver->UserData);
+	    } else {
+		iObserver->NotifyError(buffer.iStatus);
 	    }
 	    break;
 
@@ -199,7 +232,8 @@
 	    switch (buffer.iCommand) {
 		case EAPSPlayData:
 		    if (buffer.iStatus == KErrUnderflow) {
-			iObserver->PlayCb(buffer);
+			iObserver->PlayCb(buffer, iObserver->UserData);
+			iWriteQ->Send(buffer);
 		    }
 		    break;
 		case EAPSPlayerInitialize:
@@ -224,14 +258,9 @@
 		// through this handler. All other callbacks will be
 		// sent from the APS main thread through EPlayCommQueue
 		case EAPSRecorderInitialize:
-		    if (buffer.iStatus == KErrNone) {
-			iObserver->InputStreamInitialized(buffer.iStatus);
-			break;
-		    }
 		case EAPSRecordData:
-		    iObserver->NotifyError(buffer.iStatus);
-		    break;
 		default:
+		    iObserver->NotifyError(buffer.iStatus);
 		    break;
 	    }
 	    break;
@@ -245,12 +274,30 @@
         SetActive();
     }
 
+    TInt RunError(TInt) {
+	return 0;
+    }
+
     // Data
     RMsgQueue<TAPSCommBuffer>	*iQ;   // (not owned)
+    RMsgQueue<TAPSCommBuffer>	*iWriteQ;   // (not owned)
     MQueueHandlerObserver	*iObserver; // (not owned)
     TQueueHandlerType            iType;
 };
 
+/*
+ * Audio setting for CPjAudioEngine.
+ */
+class CPjAudioSetting
+{
+public:
+    TFourCC		 fourcc;
+    TAPSCodecMode	 mode;
+    TBool		 plc;
+    TBool		 vad;
+    TBool		 cng;
+    TBool		 loudspk;
+};
 
 /*
  * Implementation: Symbian Input & Output Stream.
@@ -268,9 +315,10 @@
     ~CPjAudioEngine();
 
     static CPjAudioEngine *NewL(pjmedia_snd_stream *parent_strm,
-				pjmedia_snd_rec_cb rec_cb,
-				pjmedia_snd_play_cb play_cb,
-				void *user_data);
+			        PjAudioCallback rec_cb,
+				PjAudioCallback play_cb,
+				void *user_data,
+				const CPjAudioSetting &setting);
 
     TInt StartL();
     void Stop();
@@ -279,33 +327,29 @@
 
 private:
     CPjAudioEngine(pjmedia_snd_stream *parent_strm,
-		   pjmedia_snd_rec_cb rec_cb,
-		   pjmedia_snd_play_cb play_cb,
-		   void *user_data);
+		   PjAudioCallback rec_cb,
+		   PjAudioCallback play_cb,
+		   void *user_data,
+		   const CPjAudioSetting &setting);
     void ConstructL();
-    
+
     TInt InitPlayL();
     TInt InitRecL();
     TInt StartStreamL();
-    
+
     // Inherited from MQueueHandlerObserver
     virtual void InputStreamInitialized(const TInt aStatus);
     virtual void OutputStreamInitialized(const TInt aStatus);
     virtual void NotifyError(const TInt aError);
 
-    virtual void RecCb(TAPSCommBuffer &buffer);
-    virtual void PlayCb(TAPSCommBuffer &buffer);
-
     State			 state_;
     pjmedia_snd_stream		*parentStrm_;
-    pjmedia_snd_rec_cb		 recCb_;
-    pjmedia_snd_play_cb		 playCb_;
-    void			*userData_;
-    pj_uint32_t			 TsPlay_;
-    pj_uint32_t			 TsRec_;
+    CPjAudioSetting		 setting_;
 
     RAPSSession                  iSession;
-    TAPSInitSettings             iSettings;
+    TAPSInitSettings             iPlaySettings;
+    TAPSInitSettings             iRecSettings;
+
     RMsgQueue<TAPSCommBuffer>    iReadQ;
     RMsgQueue<TAPSCommBuffer>    iReadCommQ;
     RMsgQueue<TAPSCommBuffer>    iWriteQ;
@@ -314,28 +358,19 @@
     CQueueHandler		*iPlayCommHandler;
     CQueueHandler		*iRecCommHandler;
     CQueueHandler		*iRecHandler;
-    
-    static pj_uint8_t		 aps_samples_per_frame;
-    
-    pj_int16_t			*play_buf;
-    pj_uint16_t			 play_buf_len;
-    pj_uint16_t			 play_buf_start;
-    pj_int16_t			*rec_buf;
-    pj_uint16_t			 rec_buf_len;
 };
 
 
-pj_uint8_t CPjAudioEngine::aps_samples_per_frame = 0;
-
-
 CPjAudioEngine* CPjAudioEngine::NewL(pjmedia_snd_stream *parent_strm,
-				     pjmedia_snd_rec_cb rec_cb,
-				     pjmedia_snd_play_cb play_cb,
-				     void *user_data)
+				     PjAudioCallback rec_cb,
+				     PjAudioCallback play_cb,
+				     void *user_data,
+				     const CPjAudioSetting &setting)
 {
     CPjAudioEngine* self = new (ELeave) CPjAudioEngine(parent_strm,
 						       rec_cb, play_cb,
-						       user_data);
+						       user_data,
+						       setting);
     CleanupStack::PushL(self);
     self->ConstructL();
     CleanupStack::Pop(self);
@@ -343,14 +378,14 @@
 }
 
 CPjAudioEngine::CPjAudioEngine(pjmedia_snd_stream *parent_strm,
-			       pjmedia_snd_rec_cb rec_cb,
-			       pjmedia_snd_play_cb play_cb,
-			       void *user_data) 
-      : state_(STATE_NULL),
+			       PjAudioCallback rec_cb,
+			       PjAudioCallback play_cb,
+			       void *user_data,
+			       const CPjAudioSetting &setting)
+      : MQueueHandlerObserver(rec_cb, play_cb, user_data),
+	state_(STATE_NULL),
 	parentStrm_(parent_strm),
-	recCb_(rec_cb),
-	playCb_(play_cb),
-	userData_(user_data),
+	setting_(setting),
 	iPlayCommHandler(0),
 	iRecCommHandler(0),
 	iRecHandler(0)
@@ -361,10 +396,21 @@
 {
     Stop();
 
+    delete iRecHandler;
     delete iPlayCommHandler;
-    iPlayCommHandler = NULL;
     delete iRecCommHandler;
-    iRecCommHandler = NULL;
+
+    // On some devices, immediate closing after stopping may cause APS server
+    // panic KERN-EXEC 0, so let's wait for sometime before really closing
+    // the client session.
+    TTime start, now;
+    enum { APS_CLOSE_WAIT_TIME = 200 };
+    start.UniversalTime();
+    now.UniversalTime();
+    while (now.MicroSecondsFrom(start) < APS_CLOSE_WAIT_TIME * 1000) {
+	pj_symbianos_poll(-1, APS_CLOSE_WAIT_TIME);
+	now.UniversalTime();
+    }
 
     iSession.Close();
 
@@ -383,16 +429,16 @@
     if (state_ == STATE_STREAMING || state_ == STATE_READY)
 	return 0;
 
-    TInt err = iSession.InitializePlayer(iSettings);
+    TInt err = iSession.InitializePlayer(iPlaySettings);
     if (err != KErrNone) {
 	snd_perror("Failed to initialize player", err);
 	return err;
     }
 
     // Open message queues for the output stream
-    TBuf<128> buf2 = iSettings.iGlobal;
+    TBuf<128> buf2 = iPlaySettings.iGlobal;
     buf2.Append(_L("PlayQueue"));
-    TBuf<128> buf3 = iSettings.iGlobal;
+    TBuf<128> buf3 = iPlaySettings.iGlobal;
     buf3.Append(_L("PlayCommQueue"));
 
     while (iWriteQ.OpenGlobal(buf2))
@@ -401,8 +447,7 @@
 	User::After(10);
 
     // Construct message queue handler
-    iPlayCommHandler = CQueueHandler::NewL(this,
-					   &iWriteCommQ,
+    iPlayCommHandler = CQueueHandler::NewL(this, &iWriteCommQ, &iWriteQ,
 					   CQueueHandler::EPlayCommQueue);
 
     // Start observing APS callbacks on output stream message queue
@@ -417,15 +462,15 @@
 	return 0;
 
     // Initialize input stream device
-    TInt err = iSession.InitializeRecorder(iSettings);
-    if (err != KErrNone) {
+    TInt err = iSession.InitializeRecorder(iRecSettings);
+    if (err != KErrNone && err != KErrAlreadyExists) {
 	snd_perror("Failed to initialize recorder", err);
 	return err;
     }
 
-    TBuf<128> buf1 = iSettings.iGlobal;
+    TBuf<128> buf1 = iRecSettings.iGlobal;
     buf1.Append(_L("RecordQueue"));
-    TBuf<128> buf4 = iSettings.iGlobal;
+    TBuf<128> buf4 = iRecSettings.iGlobal;
     buf4.Append(_L("RecordCommQueue"));
 
     // Must wait for APS thread to finish creating message queues
@@ -436,60 +481,57 @@
 	User::After(10);
 
     // Construct message queue handlers
-    iRecCommHandler = CQueueHandler::NewL(this,
-					  &iReadCommQ,
+    iRecHandler = CQueueHandler::NewL(this, &iReadQ, NULL,
+				      CQueueHandler::ERecordQueue);
+    iRecCommHandler = CQueueHandler::NewL(this, &iReadCommQ, NULL,
 					  CQueueHandler::ERecordCommQueue);
 
     // Start observing APS callbacks from on input stream message queue
+    iRecHandler->Start();
     iRecCommHandler->Start();
-    
+
     return 0;
 }
 
 TInt CPjAudioEngine::StartL()
 {
-    TInt err = iSession.Connect();
-    if (err != KErrNone && err != KErrAlreadyExists)
-	return err;
-
     if (state_ == STATE_READY)
 	return StartStreamL();
 
-    // Even if only capturer are opened, playback thread of APS Server need 
+    // Even if only capturer are opened, playback thread of APS Server need
     // to be run(?). Since some messages will be delivered via play comm queue.
     return InitPlayL();
 }
 
 void CPjAudioEngine::Stop()
 {
-    iSession.Stop();
-
-    delete iRecHandler;
-    iRecHandler = NULL;
-
-    state_ = STATE_READY;
+    if (state_ == STATE_STREAMING) {
+	iSession.Stop();
+	state_ = STATE_READY;
+	TRACE_((THIS_FILE, "Streaming stopped"));
+    }
 }
 
 void CPjAudioEngine::ConstructL()
 {
-    iSettings.iFourCC		    = TFourCC(KMCPFourCCIdG711);
-    iSettings.iGlobal		    = APP_UID;
-    iSettings.iPriority		    = TMdaPriority(100);
-    iSettings.iPreference	    = TMdaPriorityPreference(0x05210001);
-    iSettings.iSettings.iChannels   = EMMFMono;
-    iSettings.iSettings.iSampleRate = EMMFSampleRate8000Hz;
-    iSettings.iSettings.iVolume	    = 0;
-    
-    /* play_buf size is samples per frame of parent stream. */
-    play_buf = (pj_int16_t*)pj_pool_alloc(parentStrm_->pool, 
-				          parentStrm_->samples_per_frame << 1);
-    play_buf_len = 0;
-    play_buf_start = 0;
-    
-    /* rec_buf size is samples per frame of parent stream. */
-    rec_buf  = (pj_int16_t*)pj_pool_alloc(parentStrm_->pool, 
-					  parentStrm_->samples_per_frame << 1);
-    rec_buf_len = 0;
+    // Recorder settings
+    iRecSettings.iFourCC		= setting_.fourcc;
+    iRecSettings.iGlobal		= APP_UID;
+    iRecSettings.iPriority		= TMdaPriority(100);
+    iRecSettings.iPreference		= TMdaPriorityPreference(0x05210001);
+    iRecSettings.iSettings.iChannels	= EMMFMono;
+    iRecSettings.iSettings.iSampleRate	= EMMFSampleRate8000Hz;
+
+    // Player settings
+    iPlaySettings.iFourCC		= setting_.fourcc;
+    iPlaySettings.iGlobal		= APP_UID;
+    iPlaySettings.iPriority		= TMdaPriority(100);
+    iPlaySettings.iPreference		= TMdaPriorityPreference(0x05220001);
+    iPlaySettings.iSettings.iChannels	= EMMFMono;
+    iPlaySettings.iSettings.iSampleRate = EMMFSampleRate8000Hz;
+    iPlaySettings.iSettings.iVolume	= 0;
+
+    User::LeaveIfError(iSession.Connect());
 }
 
 TInt CPjAudioEngine::StartStreamL()
@@ -497,26 +539,23 @@
     if (state_ == STATE_STREAMING)
 	return 0;
 
-    iSession.SetCng(EFalse);
-    iSession.SetVadMode(EFalse);
-    iSession.SetPlc(EFalse);
-    iSession.SetEncoderMode(EULawOr30ms);
-    iSession.SetDecoderMode(EULawOr30ms);
-    iSession.ActivateLoudspeaker(act_loudspeaker);
-
-    // Not only playback
-    if (parentStrm_->dir != PJMEDIA_DIR_PLAYBACK) {
-	iRecHandler = CQueueHandler::NewL(this, &iReadQ, 
-					  CQueueHandler::ERecordQueue);
-	iRecHandler->Start();
-	iSession.Read();
-	TRACE_((THIS_FILE, "APS recorder started"));
-    }
+    iSession.SetCng(setting_.cng);
+    iSession.SetVadMode(setting_.vad);
+    iSession.SetPlc(setting_.plc);
+    iSession.SetEncoderMode(setting_.mode);
+    iSession.SetDecoderMode(setting_.mode);
+    iSession.ActivateLoudspeaker(setting_.loudspk);
 
     // Not only capture
     if (parentStrm_->dir != PJMEDIA_DIR_CAPTURE) {
 	iSession.Write();
-	TRACE_((THIS_FILE, "APS player started"));
+	TRACE_((THIS_FILE, "Player streaming started"));
+    }
+
+    // Not only playback
+    if (parentStrm_->dir != PJMEDIA_DIR_PLAYBACK) {
+	iSession.Read();
+	TRACE_((THIS_FILE, "Recorder streaming started"));
     }
 
     state_ = STATE_STREAMING;
@@ -556,90 +595,6 @@
     snd_perror("Error from CQueueHandler", aError);
 }
 
-void CPjAudioEngine::RecCb(TAPSCommBuffer &buffer)
-{
-    pj_assert(buffer.iBuffer[0] == 1 && buffer.iBuffer[1] == 0);
-
-    /* Detect the recorder G.711 frame size, player frame size will follow 
-     * this recorder frame size. 
-     */
-    if (CPjAudioEngine::aps_samples_per_frame == 0) {
-	CPjAudioEngine::aps_samples_per_frame = buffer.iBuffer.Length() < 160?
-						 80 : 160;
-	TRACE_((THIS_FILE, "Detected APS G.711 frame size = %u samples", 
-		CPjAudioEngine::aps_samples_per_frame));
-    }
-
-    /* Decode APS buffer (coded in G.711) and put the PCM result into rec_buf.
-     * Whenever rec_buf is full, call parent stream callback.  
-     */ 
-    unsigned dec_len = 0;
-
-    while (dec_len < CPjAudioEngine::aps_samples_per_frame) {
-	unsigned tmp;
-
-	tmp = PJ_MIN(parentStrm_->samples_per_frame - rec_buf_len, 
-		     CPjAudioEngine::aps_samples_per_frame - dec_len);
-	pjmedia_ulaw_decode(&rec_buf[rec_buf_len], 
-			    buffer.iBuffer.Ptr() + 2 + dec_len, 
-			    tmp);
-	rec_buf_len += tmp;
-	dec_len += tmp;
-	
-	pj_assert(rec_buf_len <= parentStrm_->samples_per_frame);
-	
-	if (rec_buf_len == parentStrm_->samples_per_frame) {
-	    recCb_(userData_, 0, rec_buf, rec_buf_len << 1);
-	    rec_buf_len = 0;
-	}
-    }
-}
-
-void CPjAudioEngine::PlayCb(TAPSCommBuffer &buffer)
-{
-    buffer.iCommand = CQueueHandler::EAPSPlayData;
-    buffer.iStatus = 0;
-    buffer.iBuffer.Zero();
-    buffer.iBuffer.Append(1);
-    buffer.iBuffer.Append(0);
-
-    /* Send 10ms silence frame if frame size hasn't been known. */
-    if (CPjAudioEngine::aps_samples_per_frame == 0) {
-	pjmedia_zero_samples(play_buf, 80);
-	pjmedia_ulaw_encode((pj_uint8_t*)play_buf, play_buf, 80);
-	buffer.iBuffer.Append((TUint8*)play_buf, 80);
-	iWriteQ.Send(buffer);
-	return;
-    }
-    
-    unsigned enc_len = 0;
-    
-    /* Call parent stream callback to get PCM samples to play,
-     * encode the PCM samples into G.711 and put it into APS buffer. 
-     */
-    while (enc_len < CPjAudioEngine::aps_samples_per_frame) {
-	if (play_buf_len == 0) {
-	    playCb_(userData_, 0, play_buf, parentStrm_->samples_per_frame<<1);
-	    play_buf_len = parentStrm_->samples_per_frame;
-	    play_buf_start = 0;
-	}
-	
-	unsigned tmp;
-	
-	tmp = PJ_MIN(play_buf_len, 
-		     CPjAudioEngine::aps_samples_per_frame - enc_len);
-	pjmedia_ulaw_encode((pj_uint8_t*)&play_buf[play_buf_start], 
-			    &play_buf[play_buf_start], 
-			    tmp);
-	buffer.iBuffer.Append((TUint8*)&play_buf[play_buf_start], tmp);
-	enc_len += tmp;
-	play_buf_len -= tmp;
-	play_buf_start += tmp;
-    }
-
-    iWriteQ.Send(buffer);
-}
-
 //
 // End of inherited from MQueueHandlerObserver
 /////////////////////////////////////////////////////////////
@@ -649,6 +604,7 @@
 {
     if (state_ == STATE_READY || state_ == STATE_STREAMING) {
         iSession.ActivateLoudspeaker(active);
+        TRACE_((THIS_FILE, "Loudspeaker on/off: %d", active));
 	return KErrNone;
     }
     return KErrNotReady;
@@ -657,12 +613,110 @@
 //
 
 
+static void RecCbPcm(TAPSCommBuffer &buf, void *user_data)
+{
+    pjmedia_snd_stream *strm = (pjmedia_snd_stream*) user_data;
+
+    pj_assert(buf.iBuffer[0] == 1 && buf.iBuffer[1] == 0);
+
+    /* Detect the recorder G.711 frame size, player frame size will follow
+     * this recorder frame size.
+     */
+    if (aps_g711_frame_len == 0) {
+	aps_g711_frame_len = buf.iBuffer.Length() < 160? 80 : 160;
+	TRACE_((THIS_FILE, "Detected APS G.711 frame size = %u samples",
+		aps_g711_frame_len));
+    }
+
+    /* Decode APS buffer (coded in G.711) and put the PCM result into rec_buf.
+     * Whenever rec_buf is full, call parent stream callback.
+     */
+    unsigned dec_len = 0;
+
+    while (dec_len < aps_g711_frame_len) {
+	unsigned tmp;
+
+	tmp = PJ_MIN(strm->samples_per_frame - strm->rec_buf_len,
+		     aps_g711_frame_len - dec_len);
+	pjmedia_ulaw_decode(&strm->rec_buf[strm->rec_buf_len],
+			    buf.iBuffer.Ptr() + 2 + dec_len,
+			    tmp);
+	strm->rec_buf_len += tmp;
+	dec_len += tmp;
+
+	pj_assert(strm->rec_buf_len <= strm->samples_per_frame);
+
+	if (strm->rec_buf_len == strm->samples_per_frame) {
+	    strm->rec_cb(strm->user_data, 0, strm->rec_buf,
+			 strm->rec_buf_len << 1);
+	    strm->rec_buf_len = 0;
+	}
+    }
+}
+
+static void PlayCbPcm(TAPSCommBuffer &buf, void *user_data)
+{
+    pjmedia_snd_stream *strm = (pjmedia_snd_stream*) user_data;
+    unsigned g711_frame_len = aps_g711_frame_len;
+
+    buf.iCommand = CQueueHandler::EAPSPlayData;
+    buf.iStatus = 0;
+    buf.iBuffer.Zero();
+    buf.iBuffer.Append(1);
+    buf.iBuffer.Append(0);
+
+    /* Assume frame size is 10ms if frame size hasn't been known. */
+    if (g711_frame_len == 0)
+	g711_frame_len = 80;
+
+    /* Call parent stream callback to get PCM samples to play,
+     * encode the PCM samples into G.711 and put it into APS buffer.
+     */
+    unsigned enc_len = 0;
+    while (enc_len < g711_frame_len) {
+	if (strm->play_buf_len == 0) {
+	    strm->play_cb(strm->user_data, 0, strm->play_buf,
+			  strm->samples_per_frame<<1);
+	    strm->play_buf_len = strm->samples_per_frame;
+	    strm->play_buf_start = 0;
+	}
+
+	unsigned tmp;
+
+	tmp = PJ_MIN(strm->play_buf_len, g711_frame_len - enc_len);
+	pjmedia_ulaw_encode((pj_uint8_t*)&strm->play_buf[strm->play_buf_start],
+			    &strm->play_buf[strm->play_buf_start],
+			    tmp);
+	buf.iBuffer.Append((TUint8*)&strm->play_buf[strm->play_buf_start], tmp);
+	enc_len += tmp;
+	strm->play_buf_len -= tmp;
+	strm->play_buf_start += tmp;
+    }
+}
+
+static void RecCb(TAPSCommBuffer &buf, void *user_data)
+{
+}
+
+static void PlayCb(TAPSCommBuffer &buf, void *user_data)
+{
+}
+
 /*
  * Initialize sound subsystem.
  */
 PJ_DEF(pj_status_t) pjmedia_snd_init(pj_pool_factory *factory)
 {
     snd_pool_factory = factory;
+
+    def_setting.format.u32 = PJMEDIA_FOURCC_L16;
+    def_setting.mode = 0;
+    def_setting.bitrate = 128000;
+    def_setting.plc = PJ_FALSE;
+    def_setting.vad = PJ_FALSE;
+    def_setting.cng = PJ_FALSE;
+    def_setting.loudspk = PJ_FALSE;
+
     return PJ_SUCCESS;
 }
 
@@ -700,21 +754,24 @@
 {
     pj_pool_t *pool;
     pjmedia_snd_stream *strm;
+    CPjAudioSetting setting;
+    PjAudioCallback aps_rec_cb;
+    PjAudioCallback aps_play_cb;
 
     PJ_ASSERT_RETURN(p_snd_strm, PJ_EINVAL);
-    PJ_ASSERT_RETURN(clock_rate == 8000 && channel_count == 1 && 
+    PJ_ASSERT_RETURN(clock_rate == 8000 && channel_count == 1 &&
 		     bits_per_sample == 16, PJ_ENOTSUP);
     PJ_ASSERT_RETURN((dir == PJMEDIA_DIR_CAPTURE_PLAYBACK && rec_cb && play_cb)
 		     || (dir == PJMEDIA_DIR_CAPTURE && rec_cb && !play_cb)
 		     || (dir == PJMEDIA_DIR_PLAYBACK && !rec_cb && play_cb),
 		     PJ_EINVAL);
 
-    pool = pj_pool_create(snd_pool_factory, POOL_NAME, POOL_SIZE, POOL_INC, 
+    pool = pj_pool_create(snd_pool_factory, POOL_NAME, POOL_SIZE, POOL_INC,
     			  NULL);
     if (!pool)
 	return PJ_ENOMEM;
 
-    strm = (pjmedia_snd_stream*) pj_pool_zalloc(pool, 
+    strm = (pjmedia_snd_stream*) pj_pool_zalloc(pool,
     						sizeof(pjmedia_snd_stream));
     strm->dir = dir;
     strm->pool = pool;
@@ -722,14 +779,68 @@
     strm->channel_count = channel_count;
     strm->samples_per_frame = samples_per_frame;
 
+    /* Set audio engine settings. */
+    if (def_setting.format.u32 == PJMEDIA_FOURCC_G711U ||
+	def_setting.format.u32 == PJMEDIA_FOURCC_L16)
+    {
+	setting.fourcc = TFourCC(KMCPFourCCIdG711);
+    } else {
+	setting.fourcc = TFourCC(def_setting.format.u32);
+    }
+
+    if (def_setting.format.u32 == PJMEDIA_FOURCC_AMR)
+    {
+	setting.mode = (TAPSCodecMode)def_setting.bitrate;
+    } else if (def_setting.format.u32 == PJMEDIA_FOURCC_G711U ||
+	       def_setting.format.u32 == PJMEDIA_FOURCC_L16   ||
+	      (def_setting.format.u32 == PJMEDIA_FOURCC_ILBC  &&
+	       def_setting.mode == 30))
+    {
+	setting.mode = EULawOr30ms;
+    } else {
+	setting.mode = EALawOr20ms;
+    }
+
+    setting.vad = def_setting.vad;
+    setting.plc = def_setting.plc;
+    setting.cng = def_setting.cng;
+    setting.loudspk = def_setting.loudspk;
+
+    if (def_setting.format.u32 == PJMEDIA_FOURCC_AMR ||
+	def_setting.format.u32 == PJMEDIA_FOURCC_G711A ||
+	def_setting.format.u32 == PJMEDIA_FOURCC_G711U ||
+	def_setting.format.u32 == PJMEDIA_FOURCC_G729 ||
+	def_setting.format.u32 == PJMEDIA_FOURCC_ILBC)
+    {
+	aps_play_cb = &PlayCb;
+	aps_rec_cb  = &RecCb;
+    } else {
+	aps_play_cb = &PlayCbPcm;
+	aps_rec_cb  = &RecCbPcm;
+    }
+
     // Create the audio engine.
-    TRAPD(err, strm->engine = CPjAudioEngine::NewL(strm, rec_cb, play_cb, 
-						   user_data));
+    TRAPD(err, strm->engine = CPjAudioEngine::NewL(strm,
+						   aps_rec_cb, aps_play_cb,
+						   strm, setting));
     if (err != KErrNone) {
-    	pj_pool_release(pool);	
+    	pj_pool_release(pool);
 	return PJ_RETURN_OS_ERROR(err);
     }
 
+    strm->rec_cb = rec_cb;
+    strm->play_cb = play_cb;
+    strm->user_data = user_data;
+
+    /* play_buf size is samples per frame. */
+    strm->play_buf = (pj_int16_t*)pj_pool_alloc(pool, samples_per_frame << 1);
+    strm->play_buf_len = 0;
+    strm->play_buf_start = 0;
+
+    /* rec_buf size is samples per frame. */
+    strm->rec_buf  = (pj_int16_t*)pj_pool_alloc(pool, samples_per_frame << 1);
+    strm->rec_buf_len = 0;
+
     // Done.
     *p_snd_strm = strm;
     return PJ_SUCCESS;
@@ -752,7 +863,7 @@
     if (index < 0) index = 0;
     PJ_ASSERT_RETURN(index == 0, PJ_EINVAL);
 
-    return sound_open(PJMEDIA_DIR_CAPTURE, clock_rate, channel_count, 
+    return sound_open(PJMEDIA_DIR_CAPTURE, clock_rate, channel_count,
 		      samples_per_frame, bits_per_sample, rec_cb, NULL,
 		      user_data, p_snd_strm);
 }
@@ -769,7 +880,7 @@
     if (index < 0) index = 0;
     PJ_ASSERT_RETURN(index == 0, PJ_EINVAL);
 
-    return sound_open(PJMEDIA_DIR_PLAYBACK, clock_rate, channel_count, 
+    return sound_open(PJMEDIA_DIR_PLAYBACK, clock_rate, channel_count,
 		      samples_per_frame, bits_per_sample, NULL, play_cb,
 		      user_data, p_snd_strm);
 }
@@ -789,7 +900,7 @@
     if (play_id < 0) play_id = 0;
     PJ_ASSERT_RETURN(play_id == 0 && rec_id == 0, PJ_EINVAL);
 
-    return sound_open(PJMEDIA_DIR_CAPTURE_PLAYBACK, clock_rate, channel_count, 
+    return sound_open(PJMEDIA_DIR_CAPTURE_PLAYBACK, clock_rate, channel_count,
 		      samples_per_frame, bits_per_sample, rec_cb, play_cb,
 		      user_data, p_snd_strm);
 }
@@ -821,13 +932,13 @@
 PJ_DEF(pj_status_t) pjmedia_snd_stream_start(pjmedia_snd_stream *stream)
 {
     PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL);
-    
+
     if (stream->engine) {
 	TInt err = stream->engine->StartL();
     	if (err != KErrNone)
     	    return PJ_RETURN_OS_ERROR(err);
     }
-    	
+
     return PJ_SUCCESS;
 }
 
@@ -835,11 +946,11 @@
 PJ_DEF(pj_status_t) pjmedia_snd_stream_stop(pjmedia_snd_stream *stream)
 {
     PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL);
-    
+
     if (stream->engine) {
     	stream->engine->Stop();
     }
-    
+
     return PJ_SUCCESS;
 }
 
@@ -847,20 +958,18 @@
 PJ_DEF(pj_status_t) pjmedia_snd_stream_close(pjmedia_snd_stream *stream)
 {
     pj_pool_t *pool;
-    
+
     PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL);
-    
-    if (stream->engine) {
-    	delete stream->engine;
-    	stream->engine = NULL;
-    }
+
+    delete stream->engine;
+    stream->engine = NULL;
 
     pool = stream->pool;
-    if (pool) {	
+    if (pool) {
     	stream->pool = NULL;
     	pj_pool_release(pool);
     }
-    
+
     return PJ_SUCCESS;
 }
 
@@ -875,7 +984,7 @@
 /*
  * Set sound latency.
  */
-PJ_DEF(pj_status_t) pjmedia_snd_set_latency(unsigned input_latency, 
+PJ_DEF(pj_status_t) pjmedia_snd_set_latency(unsigned input_latency,
 					    unsigned output_latency)
 {
     /* Nothing to do */
@@ -889,11 +998,11 @@
  * Activate/deactivate loudspeaker.
  */
 PJ_DEF(pj_status_t) pjmedia_snd_aps_activate_loudspeaker(
-					pjmedia_snd_stream *stream, 
+					pjmedia_snd_stream *stream,
 					pj_bool_t active)
 {
     if (stream == NULL) {
-	act_loudspeaker = active;
+	def_setting.loudspk = active;
     } else {
 	if (stream->engine == NULL)
 	    return PJ_EINVAL;
@@ -905,3 +1014,18 @@
 
     return PJ_SUCCESS;
 }
+
+/**
+ * Set a codec and its settings to be used on the next sound device session.
+ */
+PJ_DEF(pj_status_t) pjmedia_snd_aps_modify_setting(
+				    const pjmedia_snd_aps_setting *setting)
+{
+    PJ_ASSERT_RETURN(setting, PJ_EINVAL);
+
+    def_setting = *setting;
+
+    return PJ_SUCCESS;
+}
+
+#endif // PJMEDIA_SOUND_IMPLEMENTATION == PJMEDIA_SOUND_SYMB_APS_SOUND
diff --git a/pjsip-apps/src/symsndtest/app_main.cpp b/pjsip-apps/src/symsndtest/app_main.cpp
index 597e711..056cde1 100644
--- a/pjsip-apps/src/symsndtest/app_main.cpp
+++ b/pjsip-apps/src/symsndtest/app_main.cpp
@@ -49,7 +49,7 @@
     static wchar_t buf16[PJ_LOG_MAX_SIZE];
 
     PJ_UNUSED_ARG(level);
-    
+
     pj_ansi_to_unicode(buf, len, buf16, PJ_ARRAY_SIZE(buf16));
 
     TPtrC16 aBuf((const TUint16*)buf16, (TInt)len);
@@ -57,24 +57,24 @@
 }
 
 /* perror util */
-static void app_perror(const char *title, pj_status_t status) 
+static void app_perror(const char *title, pj_status_t status)
 {
-    char errmsg[PJ_ERR_MSG_SIZE];	
+    char errmsg[PJ_ERR_MSG_SIZE];
     pj_strerror(status, errmsg, sizeof(errmsg));
     PJ_LOG(1,(THIS_FILE, "Error: %s: %s", title, errmsg));
 }
 
 /* Application init */
-static pj_status_t app_init() 
+static pj_status_t app_init()
 {
     unsigned i, count;
     pj_status_t status;
-   
+
     /* Redirect log */
     pj_log_set_log_func((void (*)(int,const char*,int)) &log_writer);
     pj_log_set_decor(PJ_LOG_HAS_NEWLINE);
     pj_log_set_level(3);
-    
+
     /* Init pjlib */
     status = pj_init();
     if (status != PJ_SUCCESS) {
@@ -83,7 +83,7 @@
     }
 
     pj_caching_pool_init(&cp, NULL, 0);
-    
+
     /* Init sound subsystem */
     status = pjmedia_snd_init(&cp.factory);
     if (status != PJ_SUCCESS) {
@@ -92,16 +92,16 @@
     	pj_shutdown();
     	return status;
     }
-    
+
     count = pjmedia_snd_get_dev_count();
     PJ_LOG(3,(THIS_FILE, "Device count: %d", count));
     for (i=0; i<count; ++i) {
     	const pjmedia_snd_dev_info *info;
-    	
+
     	info = pjmedia_snd_get_dev_info(i);
     	PJ_LOG(3, (THIS_FILE, "%d: %s %d/%d %dHz",
     		   i, info->name, info->input_count, info->output_count,
-    		   info->default_samples_per_sec));	
+    		   info->default_samples_per_sec));
     }
 
     /* Create pool */
@@ -114,8 +114,8 @@
     }
 
     /* Init delay buffer */
-    status = pjmedia_delay_buf_create(pool, THIS_FILE, CLOCK_RATE, 
-				      SAMPLES_PER_FRAME, CHANNEL_COUNT, 
+    status = pjmedia_delay_buf_create(pool, THIS_FILE, CLOCK_RATE,
+				      SAMPLES_PER_FRAME, CHANNEL_COUNT,
 				      0, 0, &delaybuf);
     if (status != PJ_SUCCESS) {
     	app_perror("pjmedia_delay_buf_create()", status);
@@ -123,16 +123,16 @@
     	//pj_shutdown();
     	//return status;
     }
-    
+
     return PJ_SUCCESS;
 }
 
 
 /* Sound capture callback */
-static pj_status_t rec_cb(void *user_data, 
+static pj_status_t rec_cb(void *user_data,
 			  pj_uint32_t timestamp,
 			  void *input,
-			  unsigned size) 
+			  unsigned size)
 {
     PJ_UNUSED_ARG(user_data);
     PJ_UNUSED_ARG(timestamp);
@@ -153,28 +153,28 @@
 static pj_status_t play_cb(void *user_data,
 			   pj_uint32_t timestamp,
 			   void *output,
-			   unsigned size) 
+			   unsigned size)
 {
     PJ_UNUSED_ARG(user_data);
     PJ_UNUSED_ARG(timestamp);
     PJ_UNUSED_ARG(size);
-    
+
     pjmedia_delay_buf_get(delaybuf, (pj_int16_t*)output);
-    
+
     ++play_cnt;
-    return PJ_SUCCESS;	
+    return PJ_SUCCESS;
 }
 
 /* Start sound */
-static pj_status_t snd_start(unsigned flag) 
+static pj_status_t snd_start(unsigned flag)
 {
     pj_status_t status;
-    
+
     if (strm != NULL) {
     	app_perror("snd already open", PJ_EINVALIDOP);
     	return PJ_EINVALIDOP;
     }
-    
+
     if (flag==PJMEDIA_DIR_CAPTURE_PLAYBACK)
     	status = pjmedia_snd_open(-1, -1, CLOCK_RATE, CHANNEL_COUNT,
     				  SAMPLES_PER_FRAME, BITS_PER_SAMPLE,
@@ -187,7 +187,7 @@
     	status = pjmedia_snd_open_player(-1, CLOCK_RATE, CHANNEL_COUNT,
     					 SAMPLES_PER_FRAME, BITS_PER_SAMPLE,
     					 &play_cb, NULL, &strm);
-			 
+
     if (status != PJ_SUCCESS) {
     	app_perror("snd open", status);
     	return status;
@@ -210,19 +210,23 @@
 }
 
 /* Stop sound */
-static pj_status_t snd_stop() 
+static pj_status_t snd_stop()
 {
     pj_time_val now;
     pj_status_t status;
-    
+
     if (strm == NULL) {
     	app_perror("snd not open", PJ_EINVALIDOP);
     	return PJ_EINVALIDOP;
     }
-    
+
+    status = pjmedia_snd_stream_stop(strm);
+    if (status != PJ_SUCCESS) {
+    	app_perror("snd failed to stop", status);
+    }
     status = pjmedia_snd_stream_close(strm);
     strm = NULL;
-    
+
     pj_gettimeofday(&now);
     PJ_TIME_VAL_SUB(now, t_start);
 
@@ -234,11 +238,11 @@
 }
 
 /* Shutdown application */
-static void app_fini() 
+static void app_fini()
 {
     if (strm)
     	snd_stop();
-    
+
     pjmedia_snd_deinit();
     pjmedia_delay_buf_destroy(delaybuf);
     pj_pool_release(pool);
@@ -253,56 +257,55 @@
  */
 #include <e32base.h>
 
-class ConsoleUI : public CActive 
+class ConsoleUI : public CActive
 {
 public:
-    ConsoleUI(CActiveSchedulerWait *asw, CConsoleBase *con);
+    ConsoleUI(CConsoleBase *con);
 
     // Run console UI
     void Run();
 
     // Stop
     void Stop();
-    
+
 protected:
     // Cancel asynchronous read.
     void DoCancel();
 
     // Implementation: called when read has completed.
     void RunL();
-    
+
 private:
-    CActiveSchedulerWait *asw_;
     CConsoleBase *con_;
 };
 
 
-ConsoleUI::ConsoleUI(CActiveSchedulerWait *asw, CConsoleBase *con) 
-: CActive(EPriorityHigh), asw_(asw), con_(con)
+ConsoleUI::ConsoleUI(CConsoleBase *con)
+: CActive(EPriorityUserInput), con_(con)
 {
     CActiveScheduler::Add(this);
 }
 
 // Run console UI
-void ConsoleUI::Run() 
+void ConsoleUI::Run()
 {
     con_->Read(iStatus);
     SetActive();
 }
 
 // Stop console UI
-void ConsoleUI::Stop() 
+void ConsoleUI::Stop()
 {
     DoCancel();
 }
 
 // Cancel asynchronous read.
-void ConsoleUI::DoCancel() 
+void ConsoleUI::DoCancel()
 {
     con_->ReadCancel();
 }
 
-static void PrintMenu() 
+static void PrintMenu()
 {
     PJ_LOG(3, (THIS_FILE, "\n\n"
 	    "Menu:\n"
@@ -314,14 +317,15 @@
 }
 
 // Implementation: called when read has completed.
-void ConsoleUI::RunL() 
+void ConsoleUI::RunL()
 {
     TKeyCode kc = con_->KeyCode();
     pj_bool_t reschedule = PJ_TRUE;
-    
+
     switch (kc) {
     case 'w':
-	    asw_->AsyncStop();
+	    snd_stop();
+	    CActiveScheduler::Stop();
 	    reschedule = PJ_FALSE;
 	    break;
     case 'a':
@@ -343,30 +347,28 @@
     }
 
     PrintMenu();
-    
+
     if (reschedule)
 	Run();
 }
 
 
 ////////////////////////////////////////////////////////////////////////////
-int app_main() 
+int app_main()
 {
     if (app_init() != PJ_SUCCESS)
         return -1;
-    
+
     // Run the UI
-    CActiveSchedulerWait *asw = new CActiveSchedulerWait;
-    ConsoleUI *con = new ConsoleUI(asw, console);
-    
+    ConsoleUI *con = new ConsoleUI(console);
+
     con->Run();
-    
+
     PrintMenu();
-    asw->Start();
-    
+    CActiveScheduler::Start();
+
     delete con;
-    delete asw;
-    
+
     app_fini();
     return 0;
 }
diff --git a/pjsip-apps/src/symsndtest/main_symbian.cpp b/pjsip-apps/src/symsndtest/main_symbian.cpp
index bc160bd..a711f09 100644
--- a/pjsip-apps/src/symsndtest/main_symbian.cpp
+++ b/pjsip-apps/src/symsndtest/main_symbian.cpp
@@ -29,75 +29,12 @@
 CConsoleBase* console;
 
 // Needed by APS
-TPtrC APP_UID = _L("A000000D");
+TPtrC APP_UID = _L("A000000E");
 
 int app_main();
 
 
 ////////////////////////////////////////////////////////////////////////////
-class MyTask : public CActive
-{
-public:
-    static MyTask *NewL(CActiveSchedulerWait *asw);
-    ~MyTask();
-    void Start();
-
-protected:
-    MyTask(CActiveSchedulerWait *asw);
-    void ConstructL();
-    virtual void RunL();
-    virtual void DoCancel();
-
-private:
-    RTimer timer_;
-    CActiveSchedulerWait *asw_;
-};
-
-MyTask::MyTask(CActiveSchedulerWait *asw)
-: CActive(EPriorityNormal), asw_(asw)
-{
-}
-
-MyTask::~MyTask() 
-{
-    timer_.Close();
-}
-
-void MyTask::ConstructL()
-{
-    timer_.CreateLocal();
-    CActiveScheduler::Add(this);
-}
-
-MyTask *MyTask::NewL(CActiveSchedulerWait *asw)
-{
-    MyTask *self = new (ELeave) MyTask(asw);
-    CleanupStack::PushL(self);
-
-    self->ConstructL();
-
-    CleanupStack::Pop(self);
-    return self;
-}
-
-void MyTask::Start()
-{
-    timer_.After(iStatus, 0);
-    SetActive();
-}
-
-void MyTask::RunL()
-{
-    int rc = app_main();
-    asw_->AsyncStop();
-}
-
-void MyTask::DoCancel()
-{
-
-}
-
-////////////////////////////////////////////////////////////////////////////
 
 LOCAL_C void DoStartL()
 {
@@ -105,19 +42,8 @@
     CleanupStack::PushL(scheduler);
     CActiveScheduler::Install(scheduler);
 
-    CActiveSchedulerWait *asw = new CActiveSchedulerWait;
-    CleanupStack::PushL(asw);
-    
-    MyTask *task = MyTask::NewL(asw);
-    task->Start();
+    app_main();
 
-    asw->Start();
-    
-    delete task;
-    
-    CleanupStack::Pop(asw);
-    delete asw;
-    
     CActiveScheduler::Install(NULL);
     CleanupStack::Pop(scheduler);
     delete scheduler;
@@ -142,13 +68,13 @@
 
     TRAPD(startError, DoStartL());
 
-    console->Printf(_L("[press any key to close]\n"));
-    console->Getch();
-    
+    //console->Printf(_L("[press any key to close]\n"));
+    //console->Getch();
+
     delete console;
     delete cleanup;
 
-    CloseSTDLIB(); 
+    CloseSTDLIB();
 
     // Mark end of heap usage, detect memory leaks
     __UHEAP_MARKEND;