Index: pjproject/trunk/pjmedia/include/pjmedia/config.h
===================================================================
--- pjproject/trunk/pjmedia/include/pjmedia/config.h	(revision 887)
+++ pjproject/trunk/pjmedia/include/pjmedia/config.h	(revision 888)
@@ -175,12 +175,37 @@
  * Specify how long (in miliseconds) the stream should suspend the
  * silence detector/voice activity detector (VAD) during the initial
- * period of the session.
+ * period of the session. This feature is useful to open bindings in
+ * all NAT routers between local and remote endpoint since most NATs
+ * do not allow incoming packet to get in before local endpoint sends
+ * outgoing packets.
  *
  * Specify zero to disable this feature.
  *
- * Default: 600 msec
+ * Default: 600 msec (which gives good probability that some RTP 
+ *                    packets will reach the destination, but without
+ *                    filling up the jitter buffer on the remote end).
  */
 #ifndef PJMEDIA_STREAM_VAD_SUSPEND_MSEC
 #   define PJMEDIA_STREAM_VAD_SUSPEND_MSEC	600
+#endif
+
+
+/**
+ * Specify the maximum duration of silence period in the codec. 
+ * This is useful for example to keep NAT binding open in the firewall
+ * and to prevent server from disconnecting the call because no 
+ * RTP packet is received.
+ *
+ * This only applies to codecs that use PJMEDIA's VAD (pretty much
+ * everything including iLBC, except Speex, which has its own DTX 
+ * mechanism).
+ *
+ * Use (-1) to disable this feature.
+ *
+ * Default: 8000 (one second on 8KHz).
+ *
+ */
+#ifndef PJMEDIA_CODEC_MAX_SILENCE_PERIOD
+#   define PJMEDIA_CODEC_MAX_SILENCE_PERIOD	8000
 #endif
 
Index: pjproject/trunk/pjmedia/src/pjmedia-codec/gsm.c
===================================================================
--- pjproject/trunk/pjmedia/src/pjmedia-codec/gsm.c	(revision 887)
+++ pjproject/trunk/pjmedia/src/pjmedia-codec/gsm.c	(revision 888)
@@ -120,4 +120,5 @@
     pj_bool_t		 vad_enabled;
     pjmedia_silence_det	*vad;
+    pj_timestamp	 last_tx;
 };
 
@@ -366,4 +367,7 @@
     }
 
+    /* Re-init silence_period */
+    pj_set_timestamp32(&gsm_data->last_tx, 0, 0);
+
     /* Put in the free list. */
     pj_mutex_lock(gsm_codec_factory.mutex);
@@ -498,21 +502,29 @@
 	return PJMEDIA_CODEC_EFRMTOOSHORT;
 
-    if (input->size < 320)
-	return PJMEDIA_CODEC_EPCMTOOSHORT;
+    PJ_ASSERT_RETURN(input->size==320, PJMEDIA_CODEC_EPCMFRMINLEN);
 
     /* Detect silence */
     if (gsm_data->vad_enabled) {
 	pj_bool_t is_silence;
+	pj_int32_t silence_duration;
+
+	silence_duration = pj_timestamp_diff32(&gsm_data->last_tx, 
+					       &input->timestamp);
 
 	is_silence = pjmedia_silence_det_detect(gsm_data->vad, 
 					        input->buf,
-						input->size / 2,
+						(input->size >> 1),
 						NULL);
-	if (is_silence) {
+	if (is_silence &&
+	    PJMEDIA_CODEC_MAX_SILENCE_PERIOD != -1 &&
+	    silence_duration < PJMEDIA_CODEC_MAX_SILENCE_PERIOD) 
+	{
 	    output->type = PJMEDIA_FRAME_TYPE_NONE;
 	    output->buf = NULL;
 	    output->size = 0;
-	    output->timestamp.u64 = input->timestamp.u64;
+	    output->timestamp = input->timestamp;
 	    return PJ_SUCCESS;
+	} else {
+	    gsm_data->last_tx = input->timestamp;
 	}
     }
Index: pjproject/trunk/pjmedia/src/pjmedia-codec/ilbc.c
===================================================================
--- pjproject/trunk/pjmedia/src/pjmedia-codec/ilbc.c	(revision 887)
+++ pjproject/trunk/pjmedia/src/pjmedia-codec/ilbc.c	(revision 888)
@@ -129,4 +129,5 @@
     pj_bool_t		 vad_enabled;
     pj_bool_t		 plc_enabled;
+    pj_timestamp	 last_tx;
 
     pj_bool_t		 enc_ready;
@@ -427,4 +428,9 @@
 	return status;
 
+    /* Init last_tx (not necessary because of zalloc, but better
+     * be safe in case someone remove zalloc later.
+     */
+    pj_set_timestamp32(&ilbc_codec->last_tx, 0, 0);
+
     PJ_LOG(5,(ilbc_codec->obj_name, 
 	      "iLBC codec opened, encoder mode=%d, decoder mode=%d",
@@ -513,5 +519,5 @@
 	return PJMEDIA_CODEC_EFRMTOOSHORT;
 
-    if (input->size != ilbc_codec->enc_samples_per_frame * 2)
+    if (input->size != (ilbc_codec->enc_samples_per_frame << 1))
 	return PJMEDIA_CODEC_EPCMFRMINLEN;
 
@@ -519,15 +525,24 @@
     if (ilbc_codec->vad_enabled) {
 	pj_bool_t is_silence;
+	pj_int32_t silence_period;
+
+	silence_period = pj_timestamp_diff32(&ilbc_codec->last_tx,
+					      &input->timestamp);
 
 	is_silence = pjmedia_silence_det_detect(ilbc_codec->vad, 
 					        input->buf,
-						input->size / 2,
+						(input->size >> 1),
 						NULL);
-	if (is_silence) {
+	if (is_silence &&
+	    PJMEDIA_CODEC_MAX_SILENCE_PERIOD != -1 &&
+	    silence_period < PJMEDIA_CODEC_MAX_SILENCE_PERIOD)
+	{
 	    output->type = PJMEDIA_FRAME_TYPE_NONE;
 	    output->buf = NULL;
 	    output->size = 0;
-	    output->timestamp.u64 = input->timestamp.u64;
+	    output->timestamp = input->timestamp;
 	    return PJ_SUCCESS;
+	} else {
+	    ilbc_codec->last_tx = input->timestamp;
 	}
     }
@@ -545,5 +560,5 @@
     output->type = PJMEDIA_FRAME_TYPE_AUDIO;
     output->size = ilbc_codec->enc.no_of_bytes;
-    output->timestamp.u64 = input->timestamp.u64;
+    output->timestamp = input->timestamp;
 
     return PJ_SUCCESS;
@@ -564,5 +579,5 @@
     PJ_ASSERT_RETURN(input && output, PJ_EINVAL);
 
-    if (output_buf_len < ilbc_codec->dec_samples_per_frame*2)
+    if (output_buf_len < (ilbc_codec->dec_samples_per_frame << 1))
 	return PJMEDIA_CODEC_EPCMTOOSHORT;
 
@@ -578,7 +593,7 @@
 	((short*)output->buf)[i] = (short)ilbc_codec->dec_block[i];
     }
-    output->size = ilbc_codec->dec_samples_per_frame * 2;
+    output->size = (ilbc_codec->dec_samples_per_frame << 1);
     output->type = PJMEDIA_FRAME_TYPE_AUDIO;
-    output->timestamp.u64 = input->timestamp.u64;
+    output->timestamp = input->timestamp;
 
     return PJ_SUCCESS;
@@ -599,5 +614,5 @@
     PJ_ASSERT_RETURN(output, PJ_EINVAL);
 
-    if (output_buf_len < ilbc_codec->dec_samples_per_frame*2)
+    if (output_buf_len < (ilbc_codec->dec_samples_per_frame << 1))
 	return PJMEDIA_CODEC_EPCMTOOSHORT;
 
@@ -609,5 +624,5 @@
 	((short*)output->buf)[i] = (short)ilbc_codec->dec_block[i];
     }
-    output->size = ilbc_codec->dec_samples_per_frame * 2;
+    output->size = (ilbc_codec->dec_samples_per_frame << 1);
     output->type = PJMEDIA_FRAME_TYPE_AUDIO;
 
Index: pjproject/trunk/pjmedia/src/pjmedia/g711.c
===================================================================
--- pjproject/trunk/pjmedia/src/pjmedia/g711.c	(revision 887)
+++ pjproject/trunk/pjmedia/src/pjmedia/g711.c	(revision 888)
@@ -126,4 +126,5 @@
     pj_bool_t		 vad_enabled;
     pjmedia_silence_det *vad;
+    pj_timestamp	 last_tx;
 };
 
@@ -466,5 +467,5 @@
 
     /* Check output buffer length */
-    if (output_buf_len < input->size / 2)
+    if (output_buf_len < (input->size >> 1))
 	return PJMEDIA_CODEC_EFRMTOOSHORT;
 
@@ -472,13 +473,22 @@
     if (priv->vad_enabled) {
 	pj_bool_t is_silence;
+	pj_int32_t silence_period;
+
+	silence_period = pj_timestamp_diff32(&priv->last_tx,
+					     &input->timestamp);
 
 	is_silence = pjmedia_silence_det_detect(priv->vad, input->buf, 
-						input->size / 2, NULL);
-	if (is_silence) {
+						(input->size >> 1), NULL);
+	if (is_silence && 
+	    PJMEDIA_CODEC_MAX_SILENCE_PERIOD != -1 &&
+	    silence_period < PJMEDIA_CODEC_MAX_SILENCE_PERIOD) 
+	{
 	    output->type = PJMEDIA_FRAME_TYPE_NONE;
 	    output->buf = NULL;
 	    output->size = 0;
-	    output->timestamp.u64 = input->timestamp.u64;
+	    output->timestamp = input->timestamp;
 	    return PJ_SUCCESS;
+	} else {
+	    priv->last_tx = input->timestamp;
 	}
     }
@@ -486,15 +496,17 @@
     /* Encode */
     if (priv->pt == PJMEDIA_RTP_PT_PCMA) {
-	unsigned i;
+	unsigned i, n;
 	pj_uint8_t *dst = output->buf;
 
-	for (i=0; i!=input->size/2; ++i, ++dst) {
+	n = (input->size >> 1);
+	for (i=0; i!=n; ++i, ++dst) {
 	    *dst = pjmedia_linear2alaw(samples[i]);
 	}
     } else if (priv->pt == PJMEDIA_RTP_PT_PCMU) {
-	unsigned i;
+	unsigned i, n;
 	pj_uint8_t *dst = output->buf;
 
-	for (i=0; i!=input->size/2; ++i, ++dst) {
+	n = (input->size >> 1);
+	for (i=0; i!=n; ++i, ++dst) {
 	    *dst = pjmedia_linear2ulaw(samples[i]);
 	}
@@ -505,5 +517,5 @@
 
     output->type = PJMEDIA_FRAME_TYPE_AUDIO;
-    output->size = input->size / 2;
+    output->size = (input->size >> 1);
 
     return PJ_SUCCESS;
@@ -518,5 +530,5 @@
 
     /* Check output buffer length */
-    PJ_ASSERT_RETURN(output_buf_len >= input->size * 2,
+    PJ_ASSERT_RETURN(output_buf_len >= (input->size << 1),
 		     PJMEDIA_CODEC_EPCMTOOSHORT);
 
@@ -548,5 +560,5 @@
 
     output->type = PJMEDIA_FRAME_TYPE_AUDIO;
-    output->size = input->size * 2;
+    output->size = (input->size << 1);
 
     if (priv->plc_enabled)
Index: pjproject/trunk/pjmedia/src/pjmedia/silencedet.c
===================================================================
--- pjproject/trunk/pjmedia/src/pjmedia/silencedet.c	(revision 887)
+++ pjproject/trunk/pjmedia/src/pjmedia/silencedet.c	(revision 888)
@@ -143,5 +143,5 @@
 	min_signal = sd->ptime;
     if (recalc_time < 0)
-	recalc_time = 5000;
+	recalc_time = 2000;
 
     sd->min_signal_cnt = min_signal / sd->ptime;
@@ -257,8 +257,8 @@
 	    /* Adjust according to signal/silence proportions. */
 	    if (pct_signal > 95) {
-		new_threshold += (sd->weakest_signal - sd->cur_threshold)/4;
+		new_threshold += (sd->weakest_signal+1 - sd->cur_threshold)/2;
 	    } else if (pct_signal < 5) {
 		new_threshold = (sd->cur_threshold+sd->loudest_silence)/2+1;
-	    } else if (pct_signal > 90) {
+	    } else if (pct_signal > 80) {
 		new_threshold++;
 	    } else if (pct_signal < 10) {
@@ -269,7 +269,10 @@
 
 	    if (updated && sd->cur_threshold != new_threshold) {
+		PJ_LOG(5,(sd->objname, 
+			  "Vad cur_threshold updated %d-->%d. "
+			  "Signal lo=%d",
+			  sd->cur_threshold, new_threshold,
+			  sd->weakest_signal));
 		sd->cur_threshold = new_threshold;
-		PJ_LOG(5,(sd->objname, "Vad cur_threshold updated to %d",
-			  sd->cur_threshold));
 	    }
 	}
Index: pjproject/trunk/pjmedia/src/pjmedia/stream.c
===================================================================
--- pjproject/trunk/pjmedia/src/pjmedia/stream.c	(revision 887)
+++ pjproject/trunk/pjmedia/src/pjmedia/stream.c	(revision 888)
@@ -562,11 +562,19 @@
 
     } else if (frame->type != PJMEDIA_FRAME_TYPE_NONE) {
-	unsigned ts;
+	unsigned ts, codec_samples_per_frame;
 
 	/* Repeatedly call encode if there are multiple frames to be
 	 * sent.
 	 */
-
-	for (ts=0; ts<ts_len; ts += samples_per_frame) {
+	codec_samples_per_frame = stream->codec_param.info.enc_ptime *
+				  stream->codec_param.info.clock_rate /
+				  1000;
+	if (codec_samples_per_frame == 0) {
+	    codec_samples_per_frame = stream->codec_param.info.frm_ptime *
+				      stream->codec_param.info.clock_rate /
+				      1000;
+	}
+
+	for (ts=0; ts<ts_len; ts += codec_samples_per_frame) {
 	    pjmedia_frame tmp_out_frame, tmp_in_frame;
 	    unsigned bytes_per_sample, max_size;
@@ -576,6 +584,7 @@
 
 	    /* Split original PCM input frame into base frame size */
+	    tmp_in_frame.timestamp.u64 = frame->timestamp.u64 + ts;
 	    tmp_in_frame.buf = ((char*)frame->buf) + ts * bytes_per_sample;
-	    tmp_in_frame.size = samples_per_frame * bytes_per_sample;
+	    tmp_in_frame.size = codec_samples_per_frame * bytes_per_sample;
 	    tmp_in_frame.type = PJMEDIA_FRAME_TYPE_AUDIO;
 
@@ -690,8 +699,28 @@
 {
     pjmedia_stream *stream = port->port_data.pdata;
-    pjmedia_frame tmp_in_frame;
+    pjmedia_frame tmp_zero_frame;
     unsigned samples_per_frame;
 
     samples_per_frame = stream->enc_samples_per_frame;
+
+    /* http://www.pjsip.org/trac/ticket/56:
+     *  when input is PJMEDIA_FRAME_TYPE_NONE, feed zero PCM frame
+     *  instead so that encoder can decide whether or not to transmit
+     *  silence frame.
+     */
+    if (frame->type == PJMEDIA_FRAME_TYPE_NONE &&
+	samples_per_frame <= ZERO_PCM_MAX_SIZE) 
+    {
+	pj_memcpy(&tmp_zero_frame, frame, sizeof(pjmedia_frame));
+	frame = &tmp_zero_frame;
+
+	tmp_zero_frame.buf = zero_frame;
+	tmp_zero_frame.size = samples_per_frame * 2;
+	tmp_zero_frame.type = PJMEDIA_FRAME_TYPE_AUDIO;
+    }
+
+#if 0
+    // This is no longer needed because each TYPE_NONE frame will
+    // be converted into zero frame above
 
     /* If VAD is temporarily disabled during creation, feed zero PCM frame
@@ -710,4 +739,5 @@
 	tmp_in_frame.type = PJMEDIA_FRAME_TYPE_AUDIO;
     }
+#endif
 
     /* If VAD is temporarily disabled during creation, enable it
