Index: pjproject/trunk/pjlib/include/pj/config_site_sample.h
===================================================================
--- pjproject/trunk/pjlib/include/pj/config_site_sample.h	(revision 1640)
+++ pjproject/trunk/pjlib/include/pj/config_site_sample.h	(revision 1641)
@@ -77,5 +77,7 @@
 #   define PJSIP_MAX_DIALOG_COUNT 	31
 #   define PJSUA_MAX_CALLS		31
-    
+
+#	define PJMEDIA_SOUND_BUFFER_COUNT 12
+
 #endif
 
Index: pjproject/trunk/pjmedia/src/pjmedia/symbian_sound.cpp
===================================================================
--- pjproject/trunk/pjmedia/src/pjmedia/symbian_sound.cpp	(revision 1640)
+++ pjproject/trunk/pjmedia/src/pjmedia/symbian_sound.cpp	(revision 1641)
@@ -65,4 +65,5 @@
 
     // Common settings.
+    pjmedia_dir		 	 dir;
     unsigned			 clock_rate;
     unsigned			 channel_count;
@@ -163,4 +164,13 @@
     pj_uint32_t		     timeStamp_;
 
+    // cache variable
+    // to avoid calculating frame length repeatedly
+    TInt			  frameLen_;
+    
+    // in some SymbianOS (e.g: OSv9.1), sometimes recorded size != requested framesize
+    // so let's provide a buffer to make sure the rec callback returns framesize as requested.
+    TUint8		     *frameRecBuf_;
+    TInt			  frameRecBufLen_;
+
     CPjAudioInputEngine(pjmedia_snd_stream *parent_strm,
 			pjmedia_snd_rec_cb rec_cb,
@@ -180,7 +190,10 @@
 					 pjmedia_snd_rec_cb rec_cb,
 					 void *user_data)
-    : state_(STATE_INACTIVE), parentStrm_(parent_strm), recCb_(rec_cb), 
+    : state_(STATE_INACTIVE), parentStrm_(parent_strm), 
+      recCb_(rec_cb), userData_(user_data), 
       iInputStream_(NULL), iStreamBuffer_(NULL), iFramePtr_(0, 0),
-      userData_(user_data), lastError_(KErrNone), timeStamp_(0)
+      lastError_(KErrNone), timeStamp_(0),
+      frameLen_(parent_strm->samples_per_frame * parent_strm->channel_count * BYTES_PER_SAMPLE),
+      frameRecBuf_(NULL), frameRecBufLen_(0)
 {
 }
@@ -189,13 +202,20 @@
 {
     Stop();
+
     delete iStreamBuffer_;
     iStreamBuffer_ = NULL;
+    
+    delete [] frameRecBuf_;
+    frameRecBuf_ = NULL;
+    frameRecBufLen_ = 0;
 }
 
 void CPjAudioInputEngine::ConstructL()
 {
-    iStreamBuffer_ = HBufC8::NewL(parentStrm_->samples_per_frame *
-				  parentStrm_->channel_count * 
-				  BYTES_PER_SAMPLE);
+    iStreamBuffer_ = HBufC8::NewL(frameLen_);
+    CleanupStack::PushL(iStreamBuffer_);
+
+    frameRecBuf_ = new TUint8[frameLen_*2];
+    CleanupStack::PushL(frameRecBuf_);
 }
 
@@ -217,4 +237,6 @@
 {
     CPjAudioInputEngine *self = NewLC(parent, rec_cb, user_data);
+    CleanupStack::Pop(self->frameRecBuf_);
+    CleanupStack::Pop(self->iStreamBuffer_);
     CleanupStack::Pop(self);
     return self;
@@ -290,9 +312,6 @@
 TPtr8 & CPjAudioInputEngine::GetFrame() 
 {
-    TInt l = parentStrm_->samples_per_frame *
-	     parentStrm_->channel_count * 
-	     BYTES_PER_SAMPLE;
-    iStreamBuffer_->Des().FillZ(l);
-    iFramePtr_.Set((TUint8*)(iStreamBuffer_->Ptr()), l, l);
+    //iStreamBuffer_->Des().FillZ(frameLen_);
+    iFramePtr_.Set((TUint8*)(iStreamBuffer_->Ptr()), frameLen_, frameLen_);
     return iFramePtr_;
 }
@@ -327,9 +346,25 @@
     }
 
-    // Call the callback.
-    recCb_(userData_, timeStamp_, (void*)aBuffer.Ptr(), aBuffer.Size());
-
-    // Increment timestamp.
-    timeStamp_ += (aBuffer.Size() * BYTES_PER_SAMPLE);
+    if (frameRecBufLen_ || aBuffer.Size() < frameLen_) {
+		pj_memcpy(frameRecBuf_ + frameRecBufLen_, (void*) aBuffer.Ptr(), aBuffer.Size());
+		frameRecBufLen_ += aBuffer.Size();
+    }
+
+    if (frameRecBufLen_) {
+    	while (frameRecBufLen_ >= frameLen_) {
+	    	// Call the callback.
+		    recCb_(userData_, timeStamp_, frameRecBuf_, frameLen_);
+		    // Increment timestamp.
+		    timeStamp_ += parentStrm_->samples_per_frame;
+
+		    frameRecBufLen_ -= frameLen_;
+		    pj_memmove(frameRecBuf_, frameRecBuf_+frameLen_, frameRecBufLen_);
+    	}
+    } else {
+    	// Call the callback.
+	    recCb_(userData_, timeStamp_, (void*) aBuffer.Ptr(), aBuffer.Size());
+	    // Increment timestamp.
+	    timeStamp_ += parentStrm_->samples_per_frame;
+    }
 
     // Record next frame
@@ -526,6 +561,6 @@
 					    iSettings.iChannels);
 
-	// set volume to 1/4th of stream max volume
-	iOutputStream_->SetVolume(iOutputStream_->MaxVolume()/4);
+	// set volume to 1/2th of stream max volume
+	iOutputStream_->SetVolume(iOutputStream_->MaxVolume()/2);
 	
 	// set stream priority to normal and time sensitive
@@ -629,4 +664,7 @@
 {
     /* Always return the default sound device */
+    if (index == (unsigned)-1)
+	index = 0;
+
     PJ_ASSERT_RETURN(index==0, NULL);
     return &symbian_snd_dev_info;
@@ -663,4 +701,5 @@
     strm = (pjmedia_snd_stream*) pj_pool_zalloc(pool, 
     						sizeof(pjmedia_snd_stream));
+    strm->dir = PJMEDIA_DIR_CAPTURE;
     strm->pool = pool;
     strm->clock_rate = clock_rate;
@@ -677,7 +716,6 @@
     if (err != KErrNone) {
     	pj_pool_release(pool);
-	return PJ_RETURN_OS_ERROR(err);
-    }
-
+    	return PJ_RETURN_OS_ERROR(err);
+    }
 
     // Done.
@@ -711,4 +749,5 @@
     strm = (pjmedia_snd_stream*) pj_pool_zalloc(pool, 
     						sizeof(pjmedia_snd_stream));
+    strm->dir = PJMEDIA_DIR_PLAYBACK;    
     strm->pool = pool;
     strm->clock_rate = clock_rate;
@@ -762,4 +801,5 @@
     strm = (pjmedia_snd_stream*) pj_pool_zalloc(pool, 
     						sizeof(pjmedia_snd_stream));
+    strm->dir = PJMEDIA_DIR_CAPTURE_PLAYBACK;
     strm->pool = pool;
     strm->clock_rate = clock_rate;
@@ -795,4 +835,26 @@
 }
 
+/*
+ * Get stream info.
+ */
+PJ_DEF(pj_status_t) pjmedia_snd_stream_get_info(pjmedia_snd_stream *strm,
+						pjmedia_snd_stream_info *pi)
+{
+    PJ_ASSERT_RETURN(strm && pi, PJ_EINVAL);
+
+    pj_bzero(pi, sizeof(*pi));
+    pi->dir = strm->dir;
+    pi->play_id = 0;
+    pi->rec_id = 0;
+    pi->clock_rate = strm->clock_rate;
+    pi->channel_count = strm->channel_count;
+    pi->samples_per_frame = strm->samples_per_frame;
+    pi->bits_per_sample = BYTES_PER_SAMPLE * 8;
+    pi->rec_latency = 0;
+    pi->play_latency = 0;
+
+    return PJ_SUCCESS;
+}
+
 
 PJ_DEF(pj_status_t) pjmedia_snd_stream_start(pjmedia_snd_stream *stream)
Index: pjproject/trunk/pjsip-apps/src/symbian_ua/ua.cpp
===================================================================
--- pjproject/trunk/pjsip-apps/src/symbian_ua/ua.cpp	(revision 1640)
+++ pjproject/trunk/pjsip-apps/src/symbian_ua/ua.cpp	(revision 1641)
@@ -32,5 +32,5 @@
 // Destination URI (to make call, or to subscribe presence)
 //
-#define SIP_DST_URI	"sip:192.168.0.7:5061"
+#define SIP_DST_URI	"sip:192.168.0.11:5060"
 
 //
@@ -58,5 +58,5 @@
 //
 // STUN server
-#if 1
+#if 0
 	// Use this to have the STUN server resolved normally
 #   define STUN_DOMAIN	NULL
@@ -75,5 +75,5 @@
 // Use ICE?
 //
-#define USE_ICE		1
+#define USE_ICE		0
 
 
@@ -321,4 +321,5 @@
     med_cfg.has_ioqueue = PJ_FALSE;
     med_cfg.clock_rate = 8000;
+    med_cfg.audio_frame_ptime = 40;
     med_cfg.ec_tail_len = 0;
     med_cfg.enable_ice = USE_ICE;
@@ -453,4 +454,6 @@
 	    "  D    Dump states detail\n"
 	    "  P    Dump pool factory\n"
+   	    "  l    Start loopback audio device\n"
+   	    "  L    Stop loopback audio device\n"
 	    "  m    Call " SIP_DST_URI "\n"
 	    "  a    Answer call\n"
@@ -482,4 +485,10 @@
 	    pj_pool_factory_dump(pjsua_get_pool_factory(), PJ_TRUE);
 	    break;
+    case 'l':
+		pjsua_conf_connect(0, 0);
+	    break;
+    case 'L':
+		pjsua_conf_disconnect(0, 0);
+	    break;
     case 'm':
 	    if (g_call_id != PJSUA_INVALID_ID) {
Index: pjproject/trunk/pjsip-apps/src/symsndtest/app_main.cpp
===================================================================
--- pjproject/trunk/pjsip-apps/src/symsndtest/app_main.cpp	(revision 1640)
+++ pjproject/trunk/pjsip-apps/src/symsndtest/app_main.cpp	(revision 1641)
@@ -29,7 +29,7 @@
 #define CHANNEL_COUNT		1
 #define PTIME			100
-#define SAMPLES_PER_FRAME	(2048)
+#define SAMPLES_PER_FRAME	(80)
 #define BITS_PER_SAMPLE		16
-
+#define LOOPBACK_BUFF_COUNT 100
 
 extern CConsoleBase* console;
@@ -40,4 +40,7 @@
 static pj_time_val t_start;
 
+
+static pj_int16_t buff_loopback[SAMPLES_PER_FRAME*LOOPBACK_BUFF_COUNT];
+static pj_uint32_t pointer_w, pointer_r;
 
 /* Logging callback */
@@ -71,4 +74,5 @@
     pj_log_set_log_func((void (*)(int,const char*,int)) &log_writer);
     pj_log_set_decor(PJ_LOG_HAS_NEWLINE);
+    pj_log_set_level(5);
     
     /* Init pjlib */
@@ -115,4 +119,16 @@
     PJ_UNUSED_ARG(input);
     PJ_UNUSED_ARG(size);
+
+    pj_memcpy(&buff_loopback[pointer_w*SAMPLES_PER_FRAME], input, size);
+
+    if (size != SAMPLES_PER_FRAME*2) {
+		PJ_LOG(3, (THIS_FILE, "Size captured = %u",
+	 		   size));
+		pj_bzero(&buff_loopback[pointer_w*SAMPLES_PER_FRAME]+size/2, SAMPLES_PER_FRAME*2 - size);
+    }
+
+    if (++pointer_w >= LOOPBACK_BUFF_COUNT) {
+    	pointer_w = 0;
+    }
     
     ++rec_cnt;
@@ -129,5 +145,10 @@
     PJ_UNUSED_ARG(timestamp);
     
-    pj_bzero(output, size);
+    //pj_bzero(output, size);
+    pj_memcpy(output, &buff_loopback[pointer_r*SAMPLES_PER_FRAME], SAMPLES_PER_FRAME*2);
+
+    if (++pointer_r >= LOOPBACK_BUFF_COUNT) {
+    	pointer_r = 0;
+    }
     
     ++play_cnt;
@@ -174,4 +195,7 @@
     }
 
+    pointer_w = LOOPBACK_BUFF_COUNT/2;
+    pointer_r = 0;
+    
     return PJ_SUCCESS;
 }
@@ -298,5 +322,5 @@
     case 'p':
     	snd_start(PJMEDIA_DIR_PLAYBACK);
-	break;
+    break;
     case 'c':
     	snd_stop();
