- Added new API for sound & sound port to create/open sound device with extended setting, to allow opening sound device with non-PCM format and other settings.
- Updated symbian_ua/ua.cpp to be able to reopen sound device when audio stream session is using non-PCM data/passthrough codec.
- Updated stream.c to allow it works with non-PCM data.
- Added PCMU/A frames processing into non-PCM play/record callbacks in symbian_audio_aps.cpp.
- Added passthrough codec init/deinitialization in pjsua-lib.
- Added a new pjmedia_frame_ext helper function, pjmedia_frame_ext_pop_subframes, to pop-out/remove some subframes.
- Other minor updates/fixes.




git-svn-id: https://svn.pjsip.org/repos/pjproject/branches/projects/aps-direct@2438 74dad513-b988-da41-8d7b-12977e46ad98
diff --git a/build.symbian/bld.inf b/build.symbian/bld.inf
index 4c9c13a..a07df70 100644
--- a/build.symbian/bld.inf
+++ b/build.symbian/bld.inf
@@ -20,6 +20,7 @@
 /* Codecs */
 libgsmcodec.mmp
 libspeexcodec.mmp
+libpassthroughcodec.mmp
 
 /* Sound device impl */
 symbian_audio.mmp
diff --git a/build.symbian/libpassthroughcodec.mmp b/build.symbian/libpassthroughcodec.mmp
new file mode 100644
index 0000000..c4fb277
--- /dev/null
+++ b/build.symbian/libpassthroughcodec.mmp
@@ -0,0 +1,26 @@
+TARGET		libpassthroughcodec.lib
+TARGETTYPE	lib
+
+MACRO		HAVE_CONFIG_H
+MACRO		PJ_M_I386=1
+MACRO		PJ_SYMBIAN=1
+
+//
+// GCCE optimization setting
+//
+OPTION		GCCE -O2 -fno-unit-at-a-time
+
+//
+// Passthrough codecs wrapper for pjmedia-codec
+//
+SOURCEPATH 	..\pjmedia\src\pjmedia-codec
+SOURCE		passthrough.c
+
+//
+// Header files
+//
+SYSTEMINCLUDE	..\pjmedia\include
+SYSTEMINCLUDE	..\pjlib\include 
+
+SYSTEMINCLUDE	\epoc32\include 
+SYSTEMINCLUDE	\epoc32\include\libc
diff --git a/build.symbian/symbian_ua.mmp b/build.symbian/symbian_ua.mmp
index 5f7f1f8..e21a899 100644
--- a/build.symbian/symbian_ua.mmp
+++ b/build.symbian/symbian_ua.mmp
@@ -34,7 +34,7 @@
 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		libgsmcodec.lib libspeexcodec.lib libpassthroughcodec.lib
 STATICLIBRARY		symbian_audio.lib
 
 #if SND_USE_APS
diff --git a/pjmedia/include/pjmedia-codec.h b/pjmedia/include/pjmedia-codec.h
index 3db581a..8b7c3f6 100644
--- a/pjmedia/include/pjmedia-codec.h
+++ b/pjmedia/include/pjmedia-codec.h
@@ -31,6 +31,7 @@
 #include <pjmedia-codec/ilbc.h>
 #include <pjmedia-codec/g722.h>
 #include <pjmedia-codec/ipp_codecs.h>
+#include <pjmedia-codec/passthrough.h>
 
 
 #endif	/* __PJMEDIA_CODEC_PJMEDIA_CODEC_H__ */
diff --git a/pjmedia/include/pjmedia-codec/config.h b/pjmedia/include/pjmedia-codec/config.h
index 3fa8189..49ac321 100644
--- a/pjmedia/include/pjmedia-codec/config.h
+++ b/pjmedia/include/pjmedia-codec/config.h
@@ -217,10 +217,12 @@
 
 #ifndef PJMEDIA_HAS_PASSTHROUGH_CODEC_PCMU
 #   define PJMEDIA_HAS_PASSTHROUGH_CODEC_PCMU	1
+#   undef PJMEDIA_HAS_G711_CODEC
 #endif
 
-#ifndef PJMEDIA_HAS_PASSTHROUGH_CODEC_G729
+#ifndef PJMEDIA_HAS_PASSTHROUGH_CODEC_PCMA
 #   define PJMEDIA_HAS_PASSTHROUGH_CODEC_PCMA	1
+#   undef PJMEDIA_HAS_G711_CODEC
 #endif
 
 #endif	/* __PJMEDIA_CODEC_CONFIG_H__ */
diff --git a/pjmedia/include/pjmedia/port.h b/pjmedia/include/pjmedia/port.h
index 3c08895..374b372 100644
--- a/pjmedia/include/pjmedia/port.h
+++ b/pjmedia/include/pjmedia/port.h
@@ -359,6 +359,42 @@
 }
 	
 /**
+ * Pop out first n subframes from #pjmedia_frame_ext.
+ *
+ * @param frm		    The #pjmedia_frame_ext.
+ * @param n		    Number of first subframes to be popped out.
+ *
+ * @return		    PJ_SUCCESS, or PJ_ENOTFOUND if frame is empty.
+ */
+PJ_INLINE(pj_status_t) 
+	  pjmedia_frame_ext_pop_subframes(pjmedia_frame_ext *frm, unsigned n)
+{
+    pjmedia_frame_ext_subframe *sf;
+    pj_uint8_t *move_src;
+    unsigned move_len;
+
+    if (frm->subframe_cnt <= n) {
+	frm->subframe_cnt = 0;
+	frm->samples_cnt = 0;
+	return PJ_SUCCESS;
+    }
+
+    move_src = (pj_uint8_t*)pjmedia_frame_ext_get_subframe(frm, n);
+    sf = pjmedia_frame_ext_get_subframe(frm, frm->subframe_cnt-1);
+    move_len = (pj_uint8_t*)sf - move_src + sf->bitlen/8;
+    if (sf->bitlen % 8 != 0)
+	++move_len;
+    pj_memmove((pj_uint8_t*)frm+sizeof(pjmedia_frame_ext), 
+	       move_src, move_len);
+	    
+    frm->samples_cnt = (pj_uint16_t)
+		   (frm->samples_cnt - n*frm->samples_cnt/frm->subframe_cnt);
+    frm->subframe_cnt = (pj_uint16_t) (frm->subframe_cnt - n);
+
+    return PJ_SUCCESS;
+}
+	
+/**
  * Port interface.
  */
 typedef struct pjmedia_port
diff --git a/pjmedia/include/pjmedia/sound.h b/pjmedia/include/pjmedia/sound.h
index 962bfe9..d984ec5 100644
--- a/pjmedia/include/pjmedia/sound.h
+++ b/pjmedia/include/pjmedia/sound.h
@@ -94,6 +94,22 @@
 
 
 /** 
+ * Stream setting for opening sound device with non-PCM data.
+ */
+typedef struct pjmedia_snd_setting
+{
+    pjmedia_fourcc	format;	  /**< Format (FourCC ID).	    */ 
+    pj_uint32_t		bitrate;  /**< Bitrate (bps).		    */
+    pj_uint32_t		mode;	  /**< Mode, e.g: iLBC format has
+				       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_setting;
+
+
+/** 
  * This callback is called by player stream when it needs additional data
  * to be played by the device. Application must fill in the whole of output 
  * buffer with sound samples.
@@ -273,6 +289,46 @@
 
 
 /**
+ * Create sound stream for capturing audio and/or audio playback, from the 
+ * same device. This also allows opening sound stream with extended settings, 
+ * e.g: stream format, see #pjmedia_snd_setting.
+ *
+ * @param dir		    Sound stream direction.
+ * @param rec_id	    Device index for recorder/capture stream, or
+ *			    -1 to use the first capable device.
+ * @param play_id	    Device index for playback stream, or -1 to use 
+ *			    the first capable device.
+ * @param clock_rate	    Sound device's clock rate to set.
+ * @param channel_count	    Set number of channels, 1 for mono, or 2 for
+ *			    stereo. The channel count determines the format
+ *			    of the frame.
+ * @param samples_per_frame Number of samples per frame.
+ * @param bits_per_sample   Set the number of bits per sample. The normal 
+ *			    value for this parameter is 16 bits per sample.
+ * @param rec_cb	    Callback to handle captured audio samples.
+ * @param play_cb	    Callback to be called when the sound player needs
+ *			    more audio samples to play.
+ * @param user_data	    User data to be associated with the stream.
+ * @param setting	    Sound device extended setting.
+ * @param p_snd_strm	    Pointer to receive the stream instance.
+ *
+ * @return		    PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjmedia_snd_open2( pjmedia_dir dir,
+				        int rec_id,
+				        int play_id,
+				        unsigned clock_rate,
+				        unsigned channel_count,
+				        unsigned samples_per_frame,
+				        unsigned bits_per_sample,
+				        pjmedia_snd_rec_cb rec_cb,
+				        pjmedia_snd_play_cb play_cb,
+				        void *user_data,
+					const pjmedia_snd_setting *setting,
+				        pjmedia_snd_stream **p_snd_strm);
+
+
+/**
  * Get information about live stream.
  *
  * @param strm		The stream to be queried.
diff --git a/pjmedia/include/pjmedia/sound_port.h b/pjmedia/include/pjmedia/sound_port.h
index a41357c..4c943a2 100644
--- a/pjmedia/include/pjmedia/sound_port.h
+++ b/pjmedia/include/pjmedia/sound_port.h
@@ -159,7 +159,44 @@
 						    unsigned bits_per_sample,
 						    unsigned options,
 						    pjmedia_snd_port **p_port);
-					      
+
+
+/**
+ * Create unidirectional or bidirectional sound port. This also allows
+ * creating sound port with extended settings, e.g: audio format, see 
+ * #pjmedia_snd_setting.
+ *
+ * @param pool		    Pool to allocate sound port structure.
+ * @param dir		    Sound device direction.
+ * @param rec_id	    Device index for recorder/capture stream, or
+ *			    -1 to use the first capable device.
+ * @param play_id	    Device index for playback stream, or -1 to use 
+ *			    the first capable device.
+ * @param clock_rate	    Sound device's clock rate to set.
+ * @param channel_count	    Set number of channels, 1 for mono, or 2 for
+ *			    stereo. The channel count determines the format
+ *			    of the frame.
+ * @param samples_per_frame Number of samples per frame.
+ * @param bits_per_sample   Set the number of bits per sample. The normal 
+ *			    value for this parameter is 16 bits per sample.
+ * @param setting	    Sound device extended settings, see 
+ *			    #pjmedia_snd_setting.
+ * @param p_port	    Pointer to receive the sound device port instance.
+ *
+ * @return		    PJ_SUCCESS on success, or the appropriate error
+ *			    code.
+ */
+PJ_DECL(pj_status_t) pjmedia_snd_port_create2(pj_pool_t *pool,
+					      pjmedia_dir dir,
+					      int rec_id,
+					      int play_id,
+					      unsigned clock_rate,
+					      unsigned channel_count,
+					      unsigned samples_per_frame,
+					      unsigned bits_per_sample,
+					      const pjmedia_snd_setting *setting,
+					      pjmedia_snd_port **p_port);
+
 
 /**
  * Destroy sound device port.
diff --git a/pjmedia/include/pjmedia/symbian_sound_aps.h b/pjmedia/include/pjmedia/symbian_sound_aps.h
index 4d7c8ee..2679702 100644
--- a/pjmedia/include/pjmedia/symbian_sound_aps.h
+++ b/pjmedia/include/pjmedia/symbian_sound_aps.h
@@ -53,8 +53,7 @@
  * will be routed to earpiece.
  *
  * @param stream	The sound device stream, the stream should be started 
- *			before calling this function. This param can be NULL
- *			to set the behaviour of next opened stream.
+ *			before calling this function.
  * @param active	Specify PJ_TRUE to activate loudspeaker, and PJ_FALSE
  *			otherwise.
  *
@@ -64,18 +63,6 @@
 						pjmedia_snd_stream *stream,
 						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 14786ca..eed07e6 100644
--- a/pjmedia/include/pjmedia/types.h
+++ b/pjmedia/include/pjmedia/types.h
@@ -190,7 +190,7 @@
 /**
  * FourCC packing macro.
  */
-#define PJMEDIA_FOURCC_PACK(C1, C2, C3, C4) ( C1<<24 | C2<<16 | C3<<8 | C4 )
+#define PJMEDIA_FOURCC_PACK(C1, C2, C3, C4) ( C4<<24 | C3<<16 | C2<<8 | C1 )
 
 /**
  * FourCC identifier definitions.
diff --git a/pjmedia/src/pjmedia-codec/passthrough.c b/pjmedia/src/pjmedia-codec/passthrough.c
index 400f22d..a590291 100644
--- a/pjmedia/src/pjmedia-codec/passthrough.c
+++ b/pjmedia/src/pjmedia-codec/passthrough.c
@@ -459,13 +459,13 @@
 				    (codec_desc[i].samples_per_frame * 1000 / 
 				    codec_desc[i].channel_count / 
 				    codec_desc[i].clock_rate);
-	    attr->info.format.u32 = codec_desc[i].format.u32;
+	    attr->info.format = codec_desc[i].format;
 
 	    /* Default flags. */
 	    attr->setting.frm_per_pkt = codec_desc[i].frm_per_pkt;
-	    attr->setting.plc = 1;
+	    attr->setting.plc = 0;
 	    attr->setting.penh= 0;
-	    attr->setting.vad = 1;
+	    attr->setting.vad = 0;
 	    attr->setting.cng = attr->setting.vad;
 	    attr->setting.dec_fmtp = codec_desc[i].dec_fmtp;
 
@@ -719,7 +719,7 @@
 	frames[count].size = codec_data->avg_frame_size;
 	frames[count].timestamp.u64 = ts->u64 + count*desc->samples_per_frame;
 
-	pkt = ((char*)pkt) + codec_data->avg_frame_size;
+	pkt = (pj_uint8_t*)pkt + codec_data->avg_frame_size;
 	pkt_size -= codec_data->avg_frame_size;
 
 	++count;
@@ -799,8 +799,7 @@
     struct codec_desc *desc = &codec_desc[codec_data->codec_idx];
     pjmedia_frame_ext *output_ = (pjmedia_frame_ext*) output;
 
-    /* Check if input is formatted in pjmedia_frame */
-    pj_assert(input && input->type == PJMEDIA_FRAME_TYPE_AUDIO);
+    pj_assert(input && input->size > 0);
 
 #if PJMEDIA_HAS_PASSTHROUGH_CODEC_AMR
     /* Need to rearrange the AMR bitstream, since the bitstream may not be 
@@ -817,20 +816,16 @@
 	pjmedia_codec_amr_predecode(input, setting, &frame);
     }
 #endif
+    /*
+    PJ_ASSERT_RETURN(output_buf_len >= sizeof(pjmedia_frame_ext) +
+				       sizeof(pjmedia_frame_ext_subframe) +
+				       input->size,
+		     PJMEDIA_CODEC_EFRMTOOSHORT);
+     */
 
-    pj_bzero(output_, sizeof(pjmedia_frame_ext));
-    output_->base.type = PJMEDIA_FRAME_TYPE_EXTENDED;
-    
-    if (input && input->size > 0) {
-	PJ_ASSERT_RETURN(output_buf_len >= sizeof(pjmedia_frame_ext) +
-					   sizeof(pjmedia_frame_ext_subframe) +
-					   input->size,
-			 PJMEDIA_CODEC_EFRMTOOSHORT);
-
-	pjmedia_frame_ext_append_subframe(output_, input->buf, 
-					  (pj_uint16_t)(input->size << 3),
-					  (pj_uint16_t)desc->samples_per_frame);
-    }
+    pjmedia_frame_ext_append_subframe(output_, input->buf, 
+				      (pj_uint16_t)(input->size << 3),
+				      (pj_uint16_t)desc->samples_per_frame);
 
     return PJ_SUCCESS;
 }
@@ -842,7 +837,16 @@
 				  unsigned output_buf_len, 
 				  struct pjmedia_frame *output)
 {
-    return codec_decode(codec, NULL, output_buf_len, output);
+    codec_private_t *codec_data = (codec_private_t*) codec->codec_data;
+    struct codec_desc *desc = &codec_desc[codec_data->codec_idx];
+    pjmedia_frame_ext *output_ = (pjmedia_frame_ext*) output;
+
+    PJ_UNUSED_ARG(output_buf_len);
+
+    pjmedia_frame_ext_append_subframe(output_, NULL, 0,
+				      (pj_uint16_t)desc->samples_per_frame);
+
+    return PJ_SUCCESS;
 }
 
 #endif	/* PJMEDIA_HAS_PASSTHROUGH_CODECS */
diff --git a/pjmedia/src/pjmedia/conf_switch.c b/pjmedia/src/pjmedia/conf_switch.c
index 9639948..ca34fab 100644
--- a/pjmedia/src/pjmedia/conf_switch.c
+++ b/pjmedia/src/pjmedia/conf_switch.c
@@ -996,14 +996,15 @@
 		    pjmedia_port_put_frame(cport_dst->port, 
 					   (pjmedia_frame*)f_dst);
 
-		    /* Update TX timestamp. */
-		    pj_add_timestamp32(&cport_dst->ts_tx, 
-				       cport_dst->samples_per_frame);
-
 		    /* Reset TX buffer. */
 		    f_dst->subframe_cnt = 0;
 		    f_dst->samples_cnt = 0;
 		}
+
+		/* Update TX timestamp. */
+		pj_add_timestamp32(&cport_dst->ts_tx, 
+				   cport_dst->samples_per_frame);
+
 	    }
 	}
 
@@ -1036,13 +1037,13 @@
 		if (cport_dst->port) {
 		    pjmedia_port_put_frame(cport_dst->port, frm_dst);
 
-		    /* Update TX timestamp. */
-		    pj_add_timestamp32(&cport_dst->ts_tx, 
-				       cport_dst->samples_per_frame);
-		 
 		    /* Reset TX buffer. */
 		    frm_dst->size = 0;
 		}
+
+		/* Update TX timestamp. */
+		pj_add_timestamp32(&cport_dst->ts_tx, 
+				   cport_dst->samples_per_frame);
 	    }
 	}
 
@@ -1062,15 +1063,16 @@
 
 		frm_dst->type = PJMEDIA_FRAME_TYPE_AUDIO;
 		frm_dst->size = cport_dst->samples_per_frame << 1;
-		if (cport_dst->port)
+		if (cport_dst->port) {
 		    pjmedia_port_put_frame(cport_dst->port, frm_dst);
 
+		    /* Reset TX buffer. */
+		    frm_dst->size = 0;
+		}
+
 		/* Update TX timestamp. */
 		pj_add_timestamp32(&cport_dst->ts_tx, 
 				   cport_dst->samples_per_frame);
-
-		/* Reset TX buffer. */
-		frm_dst->size = 0;
 	    }
 	} else {
 	    pjmedia_frame_ext *f_dst = (pjmedia_frame_ext*)frm_dst;
@@ -1079,29 +1081,31 @@
 		frm_dst->type = PJMEDIA_FRAME_TYPE_EXTENDED;
 		pjmedia_frame_ext_append_subframe(f_dst, NULL, 0, (pj_uint16_t)
 						  (cport_dst->samples_per_frame-
-						  f_dst->samples_cnt));
-		if (cport_dst->port)
+						   f_dst->samples_cnt));
+		if (cport_dst->port) {
 		    pjmedia_port_put_frame(cport_dst->port, frm_dst);
 
+		    /* Reset TX buffer. */
+		    f_dst->subframe_cnt = 0;
+		    f_dst->samples_cnt = 0;
+		}
+
 		/* Update TX timestamp. */
 		pj_add_timestamp32(&cport_dst->ts_tx, 
 				   cport_dst->samples_per_frame);
-
-		/* Reset TX buffer. */
-		f_dst->subframe_cnt = 0;
-		f_dst->samples_cnt = 0;
 	    }
 	}
 
 	/* Synchronize clock. */
 	while (pj_cmp_timestamp(&cport_dst->ts_clock, 
-				&cport_dst->ts_tx) >= 0)
+				&cport_dst->ts_tx) > 0)
 	{
-	    if (cport_dst->port) {
-		frm_dst->type = PJMEDIA_FRAME_TYPE_NONE;
-		frm_dst->timestamp = cport_dst->ts_tx;
+	    frm_dst->type = PJMEDIA_FRAME_TYPE_NONE;
+	    frm_dst->timestamp = cport_dst->ts_tx;
+	    if (cport_dst->port)
 		pjmedia_port_put_frame(cport_dst->port, frm_dst);
-	    }
+
+	    /* Update TX timestamp. */
 	    pj_add_timestamp32(&cport_dst->ts_tx, cport_dst->samples_per_frame);
 	}
     }
@@ -1234,7 +1238,7 @@
 	    
 	    cport->tx_level = 0;
 
-	    while (pj_cmp_timestamp(&cport->ts_clock, &cport->ts_tx) >= 0)
+	    while (pj_cmp_timestamp(&cport->ts_clock, &cport->ts_tx) > 0)
 	    {
 		if (cport->tx_setting == PJMEDIA_PORT_ENABLE) {
 		    pjmedia_frame tmp_f;
@@ -1264,36 +1268,35 @@
 	    pjmedia_frame_ext_subframe *sf;
 	    pj_uint16_t samples_per_subframe;
 	    
+	    if (f_src_->samples_cnt < this_cport->samples_per_frame) {
+		pj_bzero(this_cport->tx_buf, sizeof(pjmedia_frame_ext));
+		frame->type = PJMEDIA_FRAME_TYPE_NONE;
+		break;
+	    }
+
 	    f_dst->samples_cnt = 0;
 	    f_dst->subframe_cnt = 0;
 	    i = 0;
 	    samples_per_subframe = f_src_->samples_cnt / f_src_->subframe_cnt;
 
+
 	    while (f_dst->samples_cnt < this_cport->samples_per_frame) {
 		sf = pjmedia_frame_ext_get_subframe(f_src_, i++);
+		pj_assert(sf);
 		pjmedia_frame_ext_append_subframe(f_dst, sf->data, sf->bitlen,
 						  samples_per_subframe);
 	    }
 
 	    /* Shift left TX buffer. */
-	    sf = pjmedia_frame_ext_get_subframe(f_src_, i);
-	    if (sf) {
-		pjmedia_frame_ext_subframe *sf_end;
-		unsigned len;
-
-		sf_end = pjmedia_frame_ext_get_subframe(f_src_, 
-						    f_src_->subframe_cnt -1);
-		len = (pj_uint8_t*)sf_end - (pj_uint8_t*)sf + sf_end->bitlen/8;
-		if (sf_end->bitlen % 8 != 0)
-		    ++len;
-		pj_memmove(this_cport->tx_buf + sizeof(pjmedia_frame_ext), sf,
-			   len);
-	    }
-	    f_src_->samples_cnt = f_src_->samples_cnt - 
-				  (pj_uint16_t)(i * samples_per_subframe);
-	    f_src_->subframe_cnt = f_src_->subframe_cnt - (pj_uint16_t)i;
+	    pjmedia_frame_ext_pop_subframes(f_src_, i);
 
 	} else if (f_src->type == PJMEDIA_FRAME_TYPE_AUDIO) {
+	    if ((f_src->size>>1) < this_cport->samples_per_frame) {
+		pj_bzero(this_cport->tx_buf, sizeof(pjmedia_frame_ext));
+		frame->type = PJMEDIA_FRAME_TYPE_NONE;
+		break;
+	    }
+
 	    pjmedia_copy_samples((pj_int16_t*)frame->buf, 
 				 (pj_int16_t*)f_src->buf, 
 				 this_cport->samples_per_frame);
@@ -1306,6 +1309,10 @@
 				     (pj_int16_t*)f_src->buf + 
 				     this_cport->samples_per_frame,
 				     f_src->size >> 1);
+	} else { /* PJMEDIA_FRAME_TYPE_NONE */
+	    /* Reset TX buffer */
+	    pj_bzero(this_cport->tx_buf, sizeof(pjmedia_frame_ext));
+	    frame->type = PJMEDIA_FRAME_TYPE_NONE;
 	}
     } while (0);
 
@@ -1326,11 +1333,6 @@
     unsigned j;
     pj_int32_t level;
 
-    /* Check for correct size. */
-    PJ_ASSERT_RETURN( f->size == conf->samples_per_frame *
-				 conf->bits_per_sample / 8,
-		      PJMEDIA_ENCSAMPLESPFRAME);
-
     pj_add_timestamp32(&cport->ts_rx, cport->samples_per_frame);
     
     /* Skip if this port is muted/disabled. */
diff --git a/pjmedia/src/pjmedia/sound_port.c b/pjmedia/src/pjmedia/sound_port.c
index 3a21ee5..c3f4968 100644
--- a/pjmedia/src/pjmedia/sound_port.c
+++ b/pjmedia/src/pjmedia/sound_port.c
@@ -35,14 +35,6 @@
 
 //#define TEST_OVERFLOW_UNDERFLOW
 
-enum
-{
-    PJMEDIA_PLC_ENABLED	    = 1,
-};
-
-//#define DEFAULT_OPTIONS	PJMEDIA_PLC_ENABLED
-#define DEFAULT_OPTIONS	    0
-
 
 struct pjmedia_snd_port
 {
@@ -51,7 +43,6 @@
     pjmedia_snd_stream	*snd_stream;
     pjmedia_dir		 dir;
     pjmedia_port	*port;
-    unsigned		 options;
 
     pjmedia_echo_state	*ec_state;
     unsigned		 aec_tail_len;
@@ -66,6 +57,7 @@
     unsigned		 channel_count;
     unsigned		 samples_per_frame;
     unsigned		 bits_per_sample;
+    pjmedia_snd_setting  setting;
 
 #if PJMEDIA_SOUND_USE_DELAYBUF
     pjmedia_delay_buf	*delay_buf;
@@ -244,61 +236,120 @@
 }
 
 /*
+ * The callback called by sound player when it needs more samples to be
+ * played. This version is for non-PCM data.
+ */
+static pj_status_t play_cb_ext(/* in */   void *user_data,
+			       /* in */   pj_uint32_t timestamp,
+			       /* out */  void *output,
+			       /* out */  unsigned size)
+{
+    pjmedia_snd_port *snd_port = (pjmedia_snd_port*) user_data;
+    pjmedia_port *port;
+    pjmedia_frame *frame = (pjmedia_frame*) output;
+    pj_status_t status;
+
+    PJ_UNUSED_ARG(size);
+    PJ_UNUSED_ARG(timestamp);
+
+    /* We're risking accessing the port without holding any mutex.
+     * It's possible that port is disconnected then destroyed while
+     * we're trying to access it.
+     * But in the name of performance, we'll try this approach until
+     * someone complains when it crashes.
+     */
+    port = snd_port->port;
+    if (port == NULL) {
+	frame->type = PJMEDIA_FRAME_TYPE_NONE;
+	return PJ_SUCCESS;
+    }
+
+    status = pjmedia_port_get_frame(port, frame);
+
+    return status;
+}
+
+
+/*
+ * The callback called by sound recorder when it has finished capturing a
+ * frame. This version is for non-PCM data.
+ */
+static pj_status_t rec_cb_ext(/* in */   void *user_data,
+			      /* in */   pj_uint32_t timestamp,
+			      /* in */   void *input,
+			      /* in*/    unsigned size)
+{
+    pjmedia_snd_port *snd_port = (pjmedia_snd_port*) user_data;
+    pjmedia_port *port;
+    pjmedia_frame *frame = (pjmedia_frame*)input;
+
+    PJ_UNUSED_ARG(size);
+    PJ_UNUSED_ARG(timestamp);
+
+    /* We're risking accessing the port without holding any mutex.
+     * It's possible that port is disconnected then destroyed while
+     * we're trying to access it.
+     * But in the name of performance, we'll try this approach until
+     * someone complains when it crashes.
+     */
+    port = snd_port->port;
+    if (port == NULL)
+	return PJ_SUCCESS;
+
+    pjmedia_port_put_frame(port, frame);
+
+    return PJ_SUCCESS;
+}
+
+/*
  * Start the sound stream.
  * This may be called even when the sound stream has already been started.
  */
 static pj_status_t start_sound_device( pj_pool_t *pool,
 				       pjmedia_snd_port *snd_port )
 {
+    pjmedia_snd_rec_cb snd_rec_cb;
+    pjmedia_snd_play_cb snd_play_cb;
     pj_status_t status;
 
     /* Check if sound has been started. */
     if (snd_port->snd_stream != NULL)
 	return PJ_SUCCESS;
 
-    /* Open sound stream. */
-    if (snd_port->dir == PJMEDIA_DIR_CAPTURE) {
-	status = pjmedia_snd_open_rec( snd_port->rec_id, 
-				       snd_port->clock_rate,
-				       snd_port->channel_count,
-				       snd_port->samples_per_frame,
-				       snd_port->bits_per_sample,
-				       &rec_cb,
-				       snd_port,
-				       &snd_port->snd_stream);
+    PJ_ASSERT_RETURN(snd_port->dir == PJMEDIA_DIR_CAPTURE ||
+		     snd_port->dir == PJMEDIA_DIR_PLAYBACK ||
+		     snd_port->dir == PJMEDIA_DIR_CAPTURE_PLAYBACK,
+		     PJ_EBUG);
 
-    } else if (snd_port->dir == PJMEDIA_DIR_PLAYBACK) {
-	status = pjmedia_snd_open_player( snd_port->play_id, 
-					  snd_port->clock_rate,
-					  snd_port->channel_count,
-					  snd_port->samples_per_frame,
-					  snd_port->bits_per_sample,
-					  &play_cb,
-					  snd_port,
-					  &snd_port->snd_stream);
-
-    } else if (snd_port->dir == PJMEDIA_DIR_CAPTURE_PLAYBACK) {
-	status = pjmedia_snd_open( snd_port->rec_id, 
-				   snd_port->play_id,
-				   snd_port->clock_rate,
-				   snd_port->channel_count,
-				   snd_port->samples_per_frame,
-				   snd_port->bits_per_sample,
-				   &rec_cb,
-				   &play_cb,
-				   snd_port,
-				   &snd_port->snd_stream);
+    if (snd_port->setting.format.u32 == 0 ||
+	snd_port->setting.format.u32 == PJMEDIA_FOURCC_L16)
+    {
+	snd_rec_cb = &rec_cb;
+	snd_play_cb = &play_cb;
     } else {
-	pj_assert(!"Invalid dir");
-	status = PJ_EBUG;
+	snd_rec_cb = &rec_cb_ext;
+	snd_play_cb = &play_cb_ext;
     }
 
+    status = pjmedia_snd_open2( snd_port->dir,
+				snd_port->rec_id, 
+				snd_port->play_id,
+				snd_port->clock_rate,
+				snd_port->channel_count,
+				snd_port->samples_per_frame,
+				snd_port->bits_per_sample,
+				snd_rec_cb,
+				snd_play_cb,
+				snd_port,
+				&snd_port->setting,
+				&snd_port->snd_stream);
+
     if (status != PJ_SUCCESS)
 	return status;
 
 
 #ifdef SIMULATE_LOST_PCT
-    snd_port->options |= PJMEDIA_PLC_ENABLED;
+    snd_port->setting.plc = PJ_TRUE;
 #endif
 
     /* If we have player components, allocate buffer to save the last
@@ -306,7 +357,7 @@
      * lost concealment (PLC) algorithm.
      */
     if ((snd_port->dir & PJMEDIA_DIR_PLAYBACK) &&
-	(snd_port->options & PJMEDIA_PLC_ENABLED)) 
+	(snd_port->setting.plc)) 
     {
 	status = pjmedia_plc_create(pool, snd_port->clock_rate, 
 				    snd_port->samples_per_frame * 
@@ -373,6 +424,8 @@
 {
     pjmedia_snd_port *snd_port;
 
+    PJ_UNUSED_ARG(options);
+
     PJ_ASSERT_RETURN(pool && p_port, PJ_EINVAL);
 
     snd_port = PJ_POOL_ZALLOC_T(pool, pjmedia_snd_port);
@@ -381,7 +434,6 @@
     snd_port->rec_id = rec_id;
     snd_port->play_id = play_id;
     snd_port->dir = PJMEDIA_DIR_CAPTURE_PLAYBACK;
-    snd_port->options = options | DEFAULT_OPTIONS;
     snd_port->clock_rate = clock_rate;
     snd_port->channel_count = channel_count;
     snd_port->samples_per_frame = samples_per_frame;
@@ -428,6 +480,8 @@
 {
     pjmedia_snd_port *snd_port;
 
+    PJ_UNUSED_ARG(options);
+
     PJ_ASSERT_RETURN(pool && p_port, PJ_EINVAL);
 
     snd_port = PJ_POOL_ZALLOC_T(pool, pjmedia_snd_port);
@@ -435,7 +489,6 @@
 
     snd_port->rec_id = dev_id;
     snd_port->dir = PJMEDIA_DIR_CAPTURE;
-    snd_port->options = options | DEFAULT_OPTIONS;
     snd_port->clock_rate = clock_rate;
     snd_port->channel_count = channel_count;
     snd_port->samples_per_frame = samples_per_frame;
@@ -465,6 +518,8 @@
 {
     pjmedia_snd_port *snd_port;
 
+    PJ_UNUSED_ARG(options);
+
     PJ_ASSERT_RETURN(pool && p_port, PJ_EINVAL);
 
     snd_port = PJ_POOL_ZALLOC_T(pool, pjmedia_snd_port);
@@ -472,7 +527,6 @@
 
     snd_port->play_id = dev_id;
     snd_port->dir = PJMEDIA_DIR_PLAYBACK;
-    snd_port->options = options | DEFAULT_OPTIONS;
     snd_port->clock_rate = clock_rate;
     snd_port->channel_count = channel_count;
     snd_port->samples_per_frame = samples_per_frame;
@@ -489,6 +543,67 @@
 
 
 /*
+ * Create bidirectional port.
+ */
+PJ_DEF(pj_status_t) pjmedia_snd_port_create2(pj_pool_t *pool,
+					     pjmedia_dir dir,
+					     int rec_id,
+					     int play_id,
+					     unsigned clock_rate,
+					     unsigned channel_count,
+					     unsigned samples_per_frame,
+					     unsigned bits_per_sample,
+					     const pjmedia_snd_setting *setting,
+					     pjmedia_snd_port **p_port)
+{
+    pjmedia_snd_port *snd_port;
+
+    PJ_ASSERT_RETURN(pool && p_port, PJ_EINVAL);
+
+    snd_port = PJ_POOL_ZALLOC_T(pool, pjmedia_snd_port);
+    PJ_ASSERT_RETURN(snd_port, PJ_ENOMEM);
+
+    snd_port->dir = dir;
+    snd_port->rec_id = rec_id;
+    snd_port->play_id = play_id;
+    snd_port->dir = PJMEDIA_DIR_CAPTURE_PLAYBACK;
+    snd_port->clock_rate = clock_rate;
+    snd_port->channel_count = channel_count;
+    snd_port->samples_per_frame = samples_per_frame;
+    snd_port->bits_per_sample = bits_per_sample;
+    snd_port->setting = *setting;
+    
+#if PJMEDIA_SOUND_USE_DELAYBUF
+    if (snd_port->setting.format.u32 == 0 ||
+	snd_port->setting.format.u32 == PJMEDIA_FOURCC_L16) 
+    {
+	pj_status_t status;
+	unsigned ptime;
+    
+	ptime = samples_per_frame * 1000 / (clock_rate * channel_count);
+    
+	status = pjmedia_delay_buf_create(pool, "snd_buff", 
+					  clock_rate, samples_per_frame,
+					  channel_count,
+					  PJMEDIA_SOUND_BUFFER_COUNT * ptime,
+					  0, &snd_port->delay_buf);
+	PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
+    }
+#endif
+
+    *p_port = snd_port;
+
+
+    /* Start sound device immediately.
+     * If there's no port connected, the sound callback will return
+     * empty signal.
+     */
+    return start_sound_device( pool, snd_port );
+
+}
+
+
+/*
  * Destroy port (also destroys the sound device).
  */
 PJ_DEF(pj_status_t) pjmedia_snd_port_destroy(pjmedia_snd_port *snd_port)
diff --git a/pjmedia/src/pjmedia/stream.c b/pjmedia/src/pjmedia/stream.c
index 2e9d119..d765fb1 100644
--- a/pjmedia/src/pjmedia/stream.c
+++ b/pjmedia/src/pjmedia/stream.c
@@ -410,6 +410,116 @@
 }
 
 
+/* The other version of get_frame callback used when stream port format
+ * is non linear PCM.
+ */
+static pj_status_t get_frame_ext( pjmedia_port *port, pjmedia_frame *frame)
+{
+    pjmedia_stream *stream = (pjmedia_stream*) port->port_data.pdata;
+    pjmedia_channel *channel = stream->dec;
+    pjmedia_frame_ext *f = (pjmedia_frame_ext*)frame;
+    unsigned samples_per_frame, samples_required;
+    pj_status_t status;
+
+    /* Return no frame if channel is paused */
+    if (channel->paused) {
+	frame->type = PJMEDIA_FRAME_TYPE_NONE;
+	return PJ_SUCCESS;
+    }
+
+    /* Repeat get frame from the jitter buffer and decode the frame
+     * until we have enough frames according to codec's ptime.
+     */
+
+    samples_required = stream->port.info.samples_per_frame;
+    samples_per_frame = stream->codec_param.info.frm_ptime *
+			stream->codec_param.info.clock_rate *
+			stream->codec_param.info.channel_cnt / 
+			1000;
+
+    pj_bzero(f, sizeof(pjmedia_frame_ext));
+    f->base.type = PJMEDIA_FRAME_TYPE_EXTENDED;
+
+    while (f->samples_cnt < samples_required) {
+	char frame_type;
+	pj_size_t frame_size;
+	pj_uint32_t bit_info;
+
+	/* Lock jitter buffer mutex first */
+	pj_mutex_lock( stream->jb_mutex );
+
+	/* Get frame from jitter buffer. */
+	pjmedia_jbuf_get_frame2(stream->jb, channel->out_pkt, &frame_size,
+			        &frame_type, &bit_info);
+	
+	/* Unlock jitter buffer mutex. */
+	pj_mutex_unlock( stream->jb_mutex );
+
+	if (frame_type == PJMEDIA_JB_NORMAL_FRAME) {
+	    /* Got "NORMAL" frame from jitter buffer */
+	    pjmedia_frame frame_in;
+
+	    /* Decode */
+	    frame_in.buf = channel->out_pkt;
+	    frame_in.size = frame_size;
+	    frame_in.bit_info = bit_info;
+	    frame_in.type = PJMEDIA_FRAME_TYPE_AUDIO;
+
+	    status = stream->codec->op->decode( stream->codec, &frame_in,
+						0, frame);
+	    if (status != PJ_SUCCESS) {
+		LOGERR_((port->info.name.ptr, "codec decode() error", 
+			 status));
+		pjmedia_frame_ext_append_subframe(f, NULL, 0,
+					    (pj_uint16_t)samples_per_frame);
+	    }
+	} else {
+	    status = (*stream->codec->op->recover)(stream->codec,
+						   0, frame);
+	    if (status != PJ_SUCCESS) {
+		pjmedia_frame_ext_append_subframe(f, NULL, 0,
+					    (pj_uint16_t)samples_per_frame);
+	    }
+
+	    if (frame_type == PJMEDIA_JB_MISSING_FRAME) {
+		PJ_LOG(1,(stream->port.info.name.ptr,  "Frame lost!"));
+	    } else if (frame_type == PJMEDIA_JB_ZERO_EMPTY_FRAME) {
+		/* Jitter buffer is empty. Check if this is the first "empty" 
+		 * state.
+		 */
+		if (frame_type != stream->jb_last_frm) {
+		    pjmedia_jb_state jb_state;
+
+		    /* Report the state of jitter buffer */
+		    pjmedia_jbuf_get_state(stream->jb, &jb_state);
+		    PJ_LOG(1,(stream->port.info.name.ptr, 
+			      "Jitter buffer empty (prefetch=%d)", 
+			      jb_state.prefetch));
+		}
+	    } else {
+		pjmedia_jb_state jb_state;
+
+		/* It can only be PJMEDIA_JB_ZERO_PREFETCH frame */
+		pj_assert(frame_type == PJMEDIA_JB_ZERO_PREFETCH_FRAME);
+
+		/* Get the state of jitter buffer */
+		pjmedia_jbuf_get_state(stream->jb, &jb_state);
+
+		if (stream->jb_last_frm != frame_type) {
+		    PJ_LOG(1,(stream->port.info.name.ptr, 
+			      "Jitter buffer is bufferring (prefetch=%d)",
+			      jb_state.prefetch));
+		}
+	    }
+	}
+
+	stream->jb_last_frm = frame_type;
+    }
+
+    return PJ_SUCCESS;
+}
+
+
 /*
  * Transmit DTMF
  */
@@ -688,6 +798,9 @@
     /* Number of samples in the frame */
     if (frame->type == PJMEDIA_FRAME_TYPE_AUDIO)
 	ts_len = (frame->size >> 1) / stream->codec_param.info.channel_cnt;
+    else if (frame->type == PJMEDIA_FRAME_TYPE_EXTENDED)
+	ts_len = stream->port.info.samples_per_frame / 
+		 stream->port.info.channel_count;
     else
 	ts_len = 0;
 
@@ -754,6 +867,8 @@
      */
     } else if (frame->type == PJMEDIA_FRAME_TYPE_AUDIO &&
 	       frame->buf == NULL &&
+	       (stream->port.info.format.u32 == 0 ||
+	        stream->port.info.format.u32 == PJMEDIA_FOURCC_L16) &&
 	       (stream->dir & PJMEDIA_DIR_ENCODING) &&
 	       stream->codec_param.info.frm_ptime *
 	       stream->codec_param.info.channel_cnt *
@@ -1491,9 +1606,17 @@
     pj_strdup(pool, &stream->port.info.encoding_name, &info->fmt.encoding_name);
     stream->port.info.clock_rate = info->fmt.clock_rate;
     stream->port.info.channel_count = info->fmt.channel_cnt;
+    stream->port.info.format = info->param->info.format;
     stream->port.port_data.pdata = stream;
-    stream->port.put_frame = &put_frame;
-    stream->port.get_frame = &get_frame;
+    if (stream->port.info.format.u32 == 0 ||
+	stream->port.info.format.u32 == PJMEDIA_FOURCC_L16)
+    {
+	stream->port.put_frame = &put_frame;
+	stream->port.get_frame = &get_frame;
+    } else {
+	stream->port.put_frame = &put_frame;
+	stream->port.get_frame = &get_frame_ext;
+    }
 
 
     /* Init stream: */
diff --git a/pjmedia/src/pjmedia/symbian_sound_aps.cpp b/pjmedia/src/pjmedia/symbian_sound_aps.cpp
index 687815c..bb17e39 100644
--- a/pjmedia/src/pjmedia/symbian_sound_aps.cpp
+++ b/pjmedia/src/pjmedia/symbian_sound_aps.cpp
@@ -17,10 +17,11 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
  */
-#include <pjmedia/symbian_sound_aps.h>
 #include <pjmedia/sound.h>
+#include <pjmedia/symbian_sound_aps.h>
 #include <pjmedia/alaw_ulaw.h>
 #include <pjmedia/errno.h>
+#include <pjmedia/port.h>
 #include <pj/assert.h>
 #include <pj/log.h>
 #include <pj/math.h>
@@ -59,9 +60,6 @@
 /* App UID to open global APS queues to communicate with the APS server. */
 extern TPtrC		    APP_UID;
 
-/* Default setting */
-static pjmedia_snd_aps_setting def_setting;
-
 /* APS G.711 frame length */
 static pj_uint8_t aps_g711_frame_len;
 
@@ -87,6 +85,7 @@
     pjmedia_snd_rec_cb   rec_cb;
     pjmedia_snd_play_cb	 play_cb;
     void                *user_data;
+    pjmedia_snd_setting  setting;
 
     // Audio engine
     CPjAudioEngine	*engine;
@@ -404,13 +403,13 @@
     // 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 };
+    enum { APS_CLOSE_WAIT_TIME = 200 }; /* in msecs */
+    
     start.UniversalTime();
-    now.UniversalTime();
-    while (now.MicroSecondsFrom(start) < APS_CLOSE_WAIT_TIME * 1000) {
+    do {
 	pj_symbianos_poll(-1, APS_CLOSE_WAIT_TIME);
 	now.UniversalTime();
-    }
+    } while (now.MicroSecondsFrom(start) < APS_CLOSE_WAIT_TIME * 1000);
 
     iSession.Close();
 
@@ -617,6 +616,7 @@
 {
     pjmedia_snd_stream *strm = (pjmedia_snd_stream*) user_data;
 
+    /* Buffer has to contain normal speech. */
     pj_assert(buf.iBuffer[0] == 1 && buf.iBuffer[1] == 0);
 
     /* Detect the recorder G.711 frame size, player frame size will follow
@@ -659,6 +659,7 @@
     pjmedia_snd_stream *strm = (pjmedia_snd_stream*) user_data;
     unsigned g711_frame_len = aps_g711_frame_len;
 
+    /* Init buffer attributes and header. */
     buf.iCommand = CQueueHandler::EAPSPlayData;
     buf.iStatus = 0;
     buf.iBuffer.Zero();
@@ -696,10 +697,117 @@
 
 static void RecCb(TAPSCommBuffer &buf, void *user_data)
 {
+    pjmedia_snd_stream *strm = (pjmedia_snd_stream*) user_data;
+    pjmedia_frame_ext *frame = (pjmedia_frame_ext*) strm->rec_buf;
+    unsigned samples_processed = 0;
+
+    switch(strm->setting.format.u32) {
+    
+    case PJMEDIA_FOURCC_G711U:
+    case PJMEDIA_FOURCC_G711A:
+	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));
+	}
+	
+	/* Convert APS buffer format into pjmedia_frame_ext. Whenever 
+	 * samples count in the frame is equal to stream's samples per frame,
+	 * call parent stream callback.
+	 */
+	while (samples_processed < aps_g711_frame_len) {
+	    unsigned tmp;
+	    const pj_uint8_t *pb = (const pj_uint8_t*)buf.iBuffer.Ptr() + 2 +
+				   samples_processed;
+
+	    tmp = PJ_MIN(strm->samples_per_frame - frame->samples_cnt,
+			 aps_g711_frame_len - samples_processed);
+	    
+	    pjmedia_frame_ext_append_subframe(frame, pb, tmp << 3, tmp);
+	    samples_processed += tmp;
+
+	    if (frame->samples_cnt == strm->samples_per_frame) {
+		frame->base.type = PJMEDIA_FRAME_TYPE_EXTENDED;
+		strm->rec_cb(strm->user_data, 0, strm->rec_buf, 0);
+		frame->samples_cnt = 0;
+		frame->subframe_cnt = 0;
+	    }
+	}
+	break;
+	
+    default:
+	break;
+    }
 }
 
 static void PlayCb(TAPSCommBuffer &buf, void *user_data)
 {
+    pjmedia_snd_stream *strm = (pjmedia_snd_stream*) user_data;
+    pjmedia_frame_ext *frame = (pjmedia_frame_ext*) strm->play_buf;
+    unsigned g711_frame_len = aps_g711_frame_len;
+    unsigned samples_ready = 0;
+
+    /* Init buffer attributes and header. */
+    buf.iCommand = CQueueHandler::EAPSPlayData;
+    buf.iStatus = 0;
+    buf.iBuffer.Zero();
+
+    switch(strm->setting.format.u32) {
+    
+    case PJMEDIA_FOURCC_G711U:
+    case PJMEDIA_FOURCC_G711A:
+	/* Add header. */
+	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 samples to play. */
+	while (samples_ready < g711_frame_len) {
+	    if (frame->samples_cnt == 0) {
+		frame->base.type = PJMEDIA_FRAME_TYPE_EXTENDED;
+		strm->play_cb(strm->user_data, 0, strm->play_buf,
+			      strm->samples_per_frame<<1);
+		
+		pj_assert(frame->base.type == PJMEDIA_FRAME_TYPE_EXTENDED ||
+			  frame->base.type == PJMEDIA_FRAME_TYPE_NONE);
+	    }
+
+	    if (frame->base.type == PJMEDIA_FRAME_TYPE_EXTENDED) { 
+		pjmedia_frame_ext_subframe *sf;
+		unsigned samples_cnt;
+		
+		sf = pjmedia_frame_ext_get_subframe(frame, 0);
+		samples_cnt = frame->samples_cnt / frame->subframe_cnt;
+		if (sf->data && sf->bitlen)
+		    buf.iBuffer.Append((TUint8*)sf->data, sf->bitlen>>3);
+		else 
+		    buf.iBuffer.AppendFill(0, samples_cnt);
+		samples_ready += samples_cnt;
+		
+		pjmedia_frame_ext_pop_subframes(frame, 1);
+	    
+	    } else { /* PJMEDIA_FRAME_TYPE_NONE */
+		buf.iBuffer.AppendFill(0, g711_frame_len - samples_ready);
+		samples_ready = g711_frame_len;
+		frame->samples_cnt = 0;
+		frame->subframe_cnt = 0;
+	    }
+	}
+	break;
+	
+    default:
+	break;
+    }
+    
+    unsigned tes = buf.iBuffer.Length();
 }
 
 /*
@@ -709,14 +817,6 @@
 {
     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;
 }
 
@@ -750,11 +850,12 @@
 			      pjmedia_snd_rec_cb rec_cb,
 			      pjmedia_snd_play_cb play_cb,
 			      void *user_data,
+			      const pjmedia_snd_setting *setting,
 			      pjmedia_snd_stream **p_snd_strm)
 {
     pj_pool_t *pool;
     pjmedia_snd_stream *strm;
-    CPjAudioSetting setting;
+    CPjAudioSetting aps_setting;
     PjAudioCallback aps_rec_cb;
     PjAudioCallback aps_play_cb;
 
@@ -778,51 +879,51 @@
     strm->clock_rate = clock_rate;
     strm->channel_count = channel_count;
     strm->samples_per_frame = samples_per_frame;
+    strm->setting = *setting;
+
+    if (strm->setting.format.u32 == 0)
+	strm->setting.format.u32 = PJMEDIA_FOURCC_L16;
 
     /* Set audio engine settings. */
-    if (def_setting.format.u32 == PJMEDIA_FOURCC_G711U ||
-	def_setting.format.u32 == PJMEDIA_FOURCC_L16)
+    if (strm->setting.format.u32 == PJMEDIA_FOURCC_G711U ||
+	strm->setting.format.u32 == PJMEDIA_FOURCC_L16)
     {
-	setting.fourcc = TFourCC(KMCPFourCCIdG711);
+	aps_setting.fourcc = TFourCC(KMCPFourCCIdG711);
     } else {
-	setting.fourcc = TFourCC(def_setting.format.u32);
+	aps_setting.fourcc = TFourCC(strm->setting.format.u32);
     }
 
-    if (def_setting.format.u32 == PJMEDIA_FOURCC_AMR)
+    if (strm->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))
+	aps_setting.mode = (TAPSCodecMode)strm->setting.bitrate;
+    } else if (strm->setting.format.u32 == PJMEDIA_FOURCC_G711U ||
+	       strm->setting.format.u32 == PJMEDIA_FOURCC_L16   ||
+	      (strm->setting.format.u32 == PJMEDIA_FOURCC_ILBC  &&
+	       strm->setting.mode == 30))
     {
-	setting.mode = EULawOr30ms;
+	aps_setting.mode = EULawOr30ms;
     } else {
-	setting.mode = EALawOr20ms;
+	aps_setting.mode = EALawOr20ms;
     }
 
-    setting.vad = def_setting.vad;
-    setting.plc = def_setting.plc;
-    setting.cng = def_setting.cng;
-    setting.loudspk = def_setting.loudspk;
+    aps_setting.vad = strm->setting.format.u32==PJMEDIA_FOURCC_L16? 
+					EFalse : strm->setting.vad;
+    aps_setting.plc = strm->setting.plc;
+    aps_setting.cng = strm->setting.cng;
+    aps_setting.loudspk = strm->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 {
+    if (strm->setting.format.u32 == PJMEDIA_FOURCC_L16) {
 	aps_play_cb = &PlayCbPcm;
 	aps_rec_cb  = &RecCbPcm;
+    } else {
+	aps_play_cb = &PlayCb;
+	aps_rec_cb  = &RecCb;
     }
 
     // Create the audio engine.
     TRAPD(err, strm->engine = CPjAudioEngine::NewL(strm,
 						   aps_rec_cb, aps_play_cb,
-						   strm, setting));
+						   strm, aps_setting));
     if (err != KErrNone) {
     	pj_pool_release(pool);
 	return PJ_RETURN_OS_ERROR(err);
@@ -833,12 +934,12 @@
     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 = (pj_int16_t*)pj_pool_zalloc(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  = (pj_int16_t*)pj_pool_zalloc(pool, samples_per_frame << 1);
     strm->rec_buf_len = 0;
 
     // Done.
@@ -860,12 +961,18 @@
 					  void *user_data,
 					  pjmedia_snd_stream **p_snd_strm)
 {
+    pjmedia_snd_setting setting;
+    
     if (index < 0) index = 0;
     PJ_ASSERT_RETURN(index == 0, PJ_EINVAL);
 
+    pj_bzero(&setting, sizeof(setting));
+    setting.format.u32 = PJMEDIA_FOURCC_L16;
+    setting.bitrate = 128000;
+    
     return sound_open(PJMEDIA_DIR_CAPTURE, clock_rate, channel_count,
 		      samples_per_frame, bits_per_sample, rec_cb, NULL,
-		      user_data, p_snd_strm);
+		      user_data, &setting, p_snd_strm);
 }
 
 PJ_DEF(pj_status_t) pjmedia_snd_open_player( int index,
@@ -877,12 +984,18 @@
 					void *user_data,
 					pjmedia_snd_stream **p_snd_strm )
 {
+    pjmedia_snd_setting setting;
+    
     if (index < 0) index = 0;
     PJ_ASSERT_RETURN(index == 0, PJ_EINVAL);
 
+    pj_bzero(&setting, sizeof(setting));
+    setting.format.u32 = PJMEDIA_FOURCC_L16;
+    setting.bitrate = 128000;
+
     return sound_open(PJMEDIA_DIR_PLAYBACK, clock_rate, channel_count,
 		      samples_per_frame, bits_per_sample, NULL, play_cb,
-		      user_data, p_snd_strm);
+		      user_data, &setting, p_snd_strm);
 }
 
 PJ_DEF(pj_status_t) pjmedia_snd_open( int rec_id,
@@ -896,13 +1009,41 @@
 				      void *user_data,
 				      pjmedia_snd_stream **p_snd_strm)
 {
+    pjmedia_snd_setting setting;
+
     if (rec_id < 0) rec_id = 0;
     if (play_id < 0) play_id = 0;
     PJ_ASSERT_RETURN(play_id == 0 && rec_id == 0, PJ_EINVAL);
 
+    pj_bzero(&setting, sizeof(setting));
+    setting.format.u32 = PJMEDIA_FOURCC_L16;
+    setting.bitrate = 128000;
+
     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);
+		      user_data, &setting, p_snd_strm);
+}
+
+PJ_DEF(pj_status_t) pjmedia_snd_open2( pjmedia_dir dir,
+				       int rec_id,
+				       int play_id,
+				       unsigned clock_rate,
+				       unsigned channel_count,
+				       unsigned samples_per_frame,
+				       unsigned bits_per_sample,
+				       pjmedia_snd_rec_cb rec_cb,
+				       pjmedia_snd_play_cb play_cb,
+				       void *user_data,
+				       const pjmedia_snd_setting *setting,
+				       pjmedia_snd_stream **p_snd_strm)
+{
+    if (rec_id < 0) rec_id = 0;
+    if (play_id < 0) play_id = 0;
+    PJ_ASSERT_RETURN(play_id == 0 && rec_id == 0, PJ_EINVAL);
+
+    return sound_open(dir, clock_rate, channel_count,
+		      samples_per_frame, bits_per_sample, rec_cb, play_cb,
+		      user_data, setting, p_snd_strm);
 }
 
 /*
@@ -993,7 +1134,6 @@
     return PJ_SUCCESS;
 }
 
-
 /*
  * Activate/deactivate loudspeaker.
  */
@@ -1001,29 +1141,11 @@
 					pjmedia_snd_stream *stream,
 					pj_bool_t active)
 {
-    if (stream == NULL) {
-	def_setting.loudspk = active;
-    } else {
-	if (stream->engine == NULL)
-	    return PJ_EINVAL;
-
-	TInt err = stream->engine->ActivateSpeaker(active);
-	if (err != KErrNone)
-	    return PJ_RETURN_OS_ERROR(err);
-    }
-
-    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;
+    PJ_ASSERT_RETURN(stream && stream->engine, PJ_EINVAL);
+    
+    TInt err = stream->engine->ActivateSpeaker(active);
+    if (err != KErrNone)
+	return PJ_RETURN_OS_ERROR(err);
 
     return PJ_SUCCESS;
 }
diff --git a/pjsip-apps/src/symbian_ua/ua.cpp b/pjsip-apps/src/symbian_ua/ua.cpp
index 442b052..531e552 100644
--- a/pjsip-apps/src/symbian_ua/ua.cpp
+++ b/pjsip-apps/src/symbian_ua/ua.cpp
@@ -90,6 +90,9 @@
 static pjsua_call_id g_call_id = PJSUA_INVALID_ID;
 static pjsua_buddy_id g_buddy_id = PJSUA_INVALID_ID;
 
+static pj_pool_t *app_pool;
+static pjmedia_snd_port *snd_port;
+
 
 /* Callback called by the library upon receiving incoming call */
 static void on_incoming_call(pjsua_acc_id acc_id, pjsua_call_id call_id,
@@ -129,6 +132,10 @@
     if (ci.state == PJSIP_INV_STATE_DISCONNECTED) {
     	if (call_id == g_call_id)
     	    g_call_id = PJSUA_INVALID_ID;
+    	if (snd_port) {
+    	    pjmedia_snd_port_destroy(snd_port);
+    	    snd_port = NULL;
+    	}
     } else if (ci.state != PJSIP_INV_STATE_INCOMING) {
     	if (g_call_id == PJSUA_INVALID_ID)
     	    g_call_id = call_id;
@@ -249,6 +256,74 @@
 			 (int)new_ci.remote_info.slen, new_ci.remote_info.ptr));
 }
 
+/* Notification that stream is created. */
+static void on_stream_created(pjsua_call_id call_id, 
+			      pjmedia_session *sess,
+			      unsigned stream_idx, 
+			      pjmedia_port **p_port)
+{
+    pjmedia_port *conf;
+    pjmedia_session_info sess_info;
+    pjmedia_stream_info *strm_info;
+    pjmedia_snd_setting setting;
+    unsigned samples_per_frame;
+    pj_status_t status;
+    
+    status = pjmedia_session_get_info(sess, &sess_info);
+    if (status != PJ_SUCCESS) {
+	PJ_LOG(1,(THIS_FILE, "on_stream_created() failed to get session info, "
+			     "status=%d", status));
+	return;
+    }
+    
+    strm_info = &sess_info.stream_info[stream_idx];
+    if (strm_info->type != PJMEDIA_TYPE_AUDIO)
+	return;
+
+    /* Don't need to reopen sound device when the session doesn't use
+     * PCM format. 
+     */
+    if (strm_info->param->info.format.u32 == 0 ||
+	strm_info->param->info.format.u32 == PJMEDIA_FOURCC_L16) 
+    {
+	return;
+    }
+    
+    pj_bzero(&setting, sizeof(setting));
+    setting.format = strm_info->param->info.format;
+    setting.bitrate = strm_info->param->info.avg_bps;
+    setting.cng = strm_info->param->setting.cng;
+    setting.vad = strm_info->param->setting.vad;
+    setting.plc = strm_info->param->setting.plc;
+
+    /* Reopen sound device. */
+    conf = pjsua_set_no_snd_dev();
+
+    samples_per_frame = conf->info.samples_per_frame;
+    
+    status = pjmedia_snd_port_create2(app_pool, 
+				      PJMEDIA_DIR_CAPTURE_PLAYBACK,
+				      0,
+				      0,
+				      8000,
+				      1,
+				      samples_per_frame,
+				      16,
+				      &setting,
+				      &snd_port);
+    if (status != PJ_SUCCESS) {
+	PJ_LOG(1,(THIS_FILE, "on_stream_created() failed to reopen sound "
+			     "device, status=%d", status));
+	return;
+    }
+    
+    status = pjmedia_snd_port_connect(snd_port, conf);
+    if (status != PJ_SUCCESS) {
+	PJ_LOG(1,(THIS_FILE, "on_stream_created() failed to connect sound "
+			     "device to conference, status=%d", status));
+	return;
+    }
+}
 
 //#include<e32debug.h>
 
@@ -289,6 +364,9 @@
     	return status;
     }
 
+    /* Create pool for application */
+    app_pool = pjsua_pool_create("pjsua-app", 1000, 1000);
+    
     /* Init pjsua */
     pjsua_config cfg;
     pjsua_logging_config log_cfg;
@@ -309,6 +387,7 @@
     cfg.cb.on_call_transfer_status = &on_call_transfer_status;
     cfg.cb.on_call_replaced = &on_call_replaced;
     cfg.cb.on_nat_detect = &on_nat_detect;
+    cfg.cb.on_stream_created = &on_stream_created;
     
     if (SIP_PROXY) {
 	    cfg.outbound_proxy_cnt = 1;
@@ -337,14 +416,10 @@
     med_cfg.thread_cnt = 0; // Disable threading on Symbian
     med_cfg.has_ioqueue = PJ_FALSE;
     med_cfg.clock_rate = 8000;
-#if defined(PJMEDIA_SYM_SND_USE_APS) && (PJMEDIA_SYM_SND_USE_APS==1)
-    med_cfg.audio_frame_ptime = 20;
-#else
     med_cfg.audio_frame_ptime = 40;
-#endif
     med_cfg.ec_tail_len = 0;
     med_cfg.enable_ice = USE_ICE;
-    med_cfg.snd_auto_close_time = 5; // wait for 5 seconds idle before sound dev get auto-closed
+    med_cfg.snd_auto_close_time = 0; // wait for 0 seconds idle before sound dev get auto-closed
     
     status = pjsua_init(&cfg, &log_cfg, &med_cfg);
     if (status != PJ_SUCCESS) {
@@ -802,6 +877,18 @@
 		  max_stack_file, max_stack_line));
 #endif
 	
+    // Let pjsua destroys app pool, since the pool may still be used by app
+    // until pjsua_destroy() finished.
+    // e.g: quitting app when there is an active call may cause sound port 
+    // memory destroyed before sound port itself gets closed/destroyed.
+    /*
+    // Release application pool
+    if (app_pool) {
+	pj_pool_release(app_pool);
+	app_pool = NULL;
+    }
+    */
+    
     // Shutdown pjsua
     pjsua_destroy();
     
diff --git a/pjsip/src/pjsua-lib/pjsua_media.c b/pjsip/src/pjsua-lib/pjsua_media.c
index 8d2d8a8..5d5d56a 100644
--- a/pjsip/src/pjsua-lib/pjsua_media.c
+++ b/pjsip/src/pjsua-lib/pjsua_media.c
@@ -172,6 +172,16 @@
 
 #endif /* PJMEDIA_HAS_INTEL_IPP */
 
+#if PJMEDIA_HAS_PASSTHROUGH_CODECS
+    /* Register passthrough codecs */
+    status = pjmedia_codec_passthrough_init(pjsua_var.med_endpt);
+    if (status != PJ_SUCCESS) {
+	pjsua_perror(THIS_FILE, "Error initializing passthrough codecs",
+		     status);
+	return status;
+    }
+#endif /* PJMEDIA_HAS_PASSTHROUGH_CODECS */
+
 #if PJMEDIA_HAS_L16_CODEC
     /* Register L16 family codecs, but disable all */
     status = pjmedia_codec_l16_init(pjsua_var.med_endpt, 0);
@@ -574,6 +584,10 @@
 	    pjmedia_codec_ipp_deinit();
 #	endif	/* PJMEDIA_HAS_INTEL_IPP */
 
+#	if PJMEDIA_HAS_PASSTHROUGH_CODECS
+	    pjmedia_codec_passthrough_deinit();
+#	endif /* PJMEDIA_HAS_PASSTHROUGH_CODECS */
+
 #	if PJMEDIA_HAS_L16_CODEC
 	    pjmedia_codec_l16_deinit();
 #	endif	/* PJMEDIA_HAS_L16_CODEC */