Index: /pjproject/trunk/pjmedia/include/pjmedia/config.h
===================================================================
--- /pjproject/trunk/pjmedia/include/pjmedia/config.h	(revision 2019)
+++ /pjproject/trunk/pjmedia/include/pjmedia/config.h	(revision 2020)
@@ -395,5 +395,5 @@
 
 /**
- * Specify the maximum duration of silence period in the codec. 
+ * Specify the maximum duration of silence period in the codec, in msec. 
  * This is useful for example to keep NAT binding open in the firewall
  * and to prevent server from disconnecting the call because no 
@@ -406,9 +406,9 @@
  * Use (-1) to disable this feature.
  *
- * Default: 8000 (one second on 8KHz).
+ * Default: 500 ms
  *
  */
 #ifndef PJMEDIA_CODEC_MAX_SILENCE_PERIOD
-#   define PJMEDIA_CODEC_MAX_SILENCE_PERIOD	8000
+#   define PJMEDIA_CODEC_MAX_SILENCE_PERIOD	500
 #endif
 
Index: /pjproject/trunk/pjmedia/src/pjmedia-codec/g722.c
===================================================================
--- /pjproject/trunk/pjmedia/src/pjmedia-codec/g722.c	(revision 2019)
+++ /pjproject/trunk/pjmedia/src/pjmedia-codec/g722.c	(revision 2020)
@@ -553,5 +553,5 @@
 	if (is_silence &&
 	    PJMEDIA_CODEC_MAX_SILENCE_PERIOD != -1 &&
-	    silence_duration < PJMEDIA_CODEC_MAX_SILENCE_PERIOD) 
+	    silence_duration < PJMEDIA_CODEC_MAX_SILENCE_PERIOD*16000/1000) 
 	{
 	    output->type = PJMEDIA_FRAME_TYPE_NONE;
Index: /pjproject/trunk/pjmedia/src/pjmedia-codec/gsm.c
===================================================================
--- /pjproject/trunk/pjmedia/src/pjmedia-codec/gsm.c	(revision 2019)
+++ /pjproject/trunk/pjmedia/src/pjmedia-codec/gsm.c	(revision 2020)
@@ -543,5 +543,5 @@
 	if (is_silence &&
 	    PJMEDIA_CODEC_MAX_SILENCE_PERIOD != -1 &&
-	    silence_duration < PJMEDIA_CODEC_MAX_SILENCE_PERIOD) 
+	    silence_duration < PJMEDIA_CODEC_MAX_SILENCE_PERIOD*8000/1000) 
 	{
 	    output->type = PJMEDIA_FRAME_TYPE_NONE;
Index: /pjproject/trunk/pjmedia/src/pjmedia-codec/ilbc.c
===================================================================
--- /pjproject/trunk/pjmedia/src/pjmedia-codec/ilbc.c	(revision 2019)
+++ /pjproject/trunk/pjmedia/src/pjmedia-codec/ilbc.c	(revision 2020)
@@ -540,5 +540,5 @@
 	if (is_silence &&
 	    PJMEDIA_CODEC_MAX_SILENCE_PERIOD != -1 &&
-	    silence_period < PJMEDIA_CODEC_MAX_SILENCE_PERIOD)
+	    silence_period < PJMEDIA_CODEC_MAX_SILENCE_PERIOD*8000/1000)
 	{
 	    output->type = PJMEDIA_FRAME_TYPE_NONE;
Index: /pjproject/trunk/pjmedia/src/pjmedia/g711.c
===================================================================
--- /pjproject/trunk/pjmedia/src/pjmedia/g711.c	(revision 2019)
+++ /pjproject/trunk/pjmedia/src/pjmedia/g711.c	(revision 2020)
@@ -503,5 +503,5 @@
 	if (is_silence && 
 	    PJMEDIA_CODEC_MAX_SILENCE_PERIOD != -1 &&
-	    silence_period < PJMEDIA_CODEC_MAX_SILENCE_PERIOD) 
+	    silence_period < PJMEDIA_CODEC_MAX_SILENCE_PERIOD*8000/1000) 
 	{
 	    output->type = PJMEDIA_FRAME_TYPE_NONE;
Index: /pjproject/trunk/pjmedia/src/pjmedia/stream.c
===================================================================
--- /pjproject/trunk/pjmedia/src/pjmedia/stream.c	(revision 2019)
+++ /pjproject/trunk/pjmedia/src/pjmedia/stream.c	(revision 2020)
@@ -84,4 +84,5 @@
     pjmedia_dir		     dir;	    /**< Stream direction.	    */
     void		    *user_data;	    /**< User data.		    */
+    pj_str_t		     cname;	    /**< SDES CNAME		    */
 
     pjmedia_transport	    *transport;	    /**< Stream transport.	    */
@@ -116,4 +117,5 @@
     pj_uint32_t		     rtcp_last_tx;  /**< RTCP tx time in timestamp  */
     pj_uint32_t		     rtcp_interval; /**< Interval, in timestamp.    */
+    pj_bool_t		     initial_rr;    /**< Initial RTCP RR sent	    */
 
     /* RFC 2833 DTMF transmission queue: */
@@ -177,4 +179,7 @@
 				   'A', 'B', 'C', 'D'};
 
+/* Zero audio frame samples */
+static pj_int16_t zero_frame[30 * 16000 / 1000];
+
 /*
  * Print error.
@@ -535,4 +540,66 @@
 }
 
+/* Build RTCP SDES packet */
+static unsigned create_rtcp_sdes(pjmedia_stream *stream, pj_uint8_t *pkt,
+				 unsigned max_len)
+{
+    pjmedia_rtcp_common hdr;
+    pj_uint8_t *p = pkt;
+
+    /* SDES header */
+    hdr.version = 2;
+    hdr.p = 0;
+    hdr.count = 1;
+    hdr.pt = 202;
+    hdr.length = 2 + (4+stream->cname.slen+3)/4 - 1;
+    if (max_len < (hdr.length << 2)) {
+	pj_assert(!"Not enough buffer for SDES packet");
+	return 0;
+    }
+    hdr.length = pj_htons((pj_uint16_t)hdr.length);
+    hdr.ssrc = stream->enc->rtp.out_hdr.ssrc;
+    pj_memcpy(p, &hdr, sizeof(hdr));
+    p += sizeof(hdr);
+
+    /* CNAME item */
+    *p++ = 1;
+    *p++ = (pj_uint8_t)stream->cname.slen;
+    pj_memcpy(p, stream->cname.ptr, stream->cname.slen);
+    p += stream->cname.slen;
+
+    /* END */
+    *p++ = '\0';
+    *p++ = '\0';
+
+    /* Pad to 32bit */
+    while ((p-pkt) % 4)
+	*p++ = '\0';
+
+    return (p - pkt);
+}
+
+/* Build RTCP BYE packet */
+static unsigned create_rtcp_bye(pjmedia_stream *stream, pj_uint8_t *pkt,
+				unsigned max_len)
+{
+    pjmedia_rtcp_common hdr;
+
+    /* BYE header */
+    hdr.version = 2;
+    hdr.p = 0;
+    hdr.count = 1;
+    hdr.pt = 203;
+    hdr.length = 1;
+    if (max_len < (hdr.length << 2)) {
+	pj_assert(!"Not enough buffer for SDES packet");
+	return 0;
+    }
+    hdr.length = pj_htons((pj_uint16_t)hdr.length);
+    hdr.ssrc = stream->enc->rtp.out_hdr.ssrc;
+    pj_memcpy(pkt, &hdr, sizeof(hdr));
+
+    return sizeof(hdr);
+}
+
 
 /**
@@ -674,7 +741,51 @@
 	}
 
-	/* No need to encode if this is a zero frame.
-	 * See http://www.pjsip.org/trac/ticket/439 
-	 */
+
+    /*
+     * Special treatment for FRAME_TYPE_AUDIO but with frame->buf==NULL.
+     * This happens when stream input is disconnected from the bridge.
+     * In this case we periodically transmit RTP frame to keep NAT binding
+     * open, by giving zero PCM frame to the codec.
+     *
+     * This was originally done in http://trac.pjsip.org/repos/ticket/56,
+     * but then disabled in http://trac.pjsip.org/repos/ticket/439, but
+     * now it's enabled again.
+     */
+    } else if (frame->type == PJMEDIA_FRAME_TYPE_AUDIO &&
+	       frame->buf == NULL &&
+	       (stream->dir & PJMEDIA_DIR_ENCODING) &&
+	       stream->codec_param.info.frm_ptime *
+		stream->codec_param.info.clock_rate/1000 <
+		  PJ_ARRAY_SIZE(zero_frame)) 
+    {
+	pjmedia_frame silence_frame;
+
+	pj_bzero(&silence_frame, sizeof(silence_frame));
+	silence_frame.buf = zero_frame;
+	silence_frame.size = stream->codec_param.info.frm_ptime * 2 *
+			      stream->codec_param.info.clock_rate / 1000;
+	silence_frame.type = PJMEDIA_FRAME_TYPE_AUDIO;
+	silence_frame.timestamp.u32.lo = pj_ntohl(stream->enc->rtp.out_hdr.ts);
+	
+	/* Encode! */
+	status = stream->codec->op->encode( stream->codec, &silence_frame,
+					    channel->out_pkt_size - 
+					    sizeof(pjmedia_rtp_hdr),
+					    &frame_out);
+	if (status != PJ_SUCCESS) {
+	    LOGERR_((stream->port.info.name.ptr, 
+		    "Codec encode() error", status));
+	    return status;
+	}
+
+	/* Encapsulate. */
+	status = pjmedia_rtp_encode_rtp( &channel->rtp, 
+					 channel->pt, 0, 
+					 frame_out.size, rtp_ts_len, 
+					 (const void**)&rtphdr, 
+					 &rtphdrlen);
+
+
+    /* Encode audio frame */
     } else if (frame->type != PJMEDIA_FRAME_TYPE_NONE &&
 	       frame->buf != NULL) 
@@ -697,4 +808,5 @@
 					 (const void**)&rtphdr, 
 					 &rtphdrlen);
+
     } else {
 
@@ -1205,4 +1317,29 @@
     pjmedia_rtcp_rx_rtp2(&stream->rtcp, pj_ntohs(hdr->seq),
 			 pj_ntohl(hdr->ts), payloadlen, pkt_discarded);
+
+    /* Send RTCP RR and SDES after we receive some RTP packets */
+    if (stream->rtcp.received >= 10 && !stream->initial_rr) {
+	void *sr_rr_pkt;
+	pj_uint8_t *pkt;
+	int len;
+
+	/* Build RR or SR */
+	pjmedia_rtcp_build_rtcp(&stream->rtcp, &sr_rr_pkt, &len);
+	pkt = (pj_uint8_t*) stream->enc->out_pkt;
+	pj_memcpy(pkt, sr_rr_pkt, len);
+	pkt += len;
+
+	/* Append SDES */
+	len = create_rtcp_sdes(stream, (pj_uint8_t*)pkt, 
+			       stream->enc->out_pkt_size - len);
+	if (len > 0) {
+	    pkt += len;
+	    len = ((pj_uint8_t*)pkt) - ((pj_uint8_t*)stream->enc->out_pkt);
+	    pjmedia_transport_send_rtcp(stream->transport, 
+					stream->enc->out_pkt, len);
+	}
+
+	stream->initial_rr = PJ_TRUE;
+    }
 }
 
@@ -1302,5 +1439,6 @@
     pjmedia_stream *stream;
     pj_str_t name;
-    unsigned jb_init, jb_max, jb_min_pre, jb_max_pre;
+    unsigned jb_init, jb_max, jb_min_pre, jb_max_pre, len;
+    char *p;
     pj_status_t status;
 
@@ -1340,5 +1478,5 @@
     stream->dir = info->dir;
     stream->user_data = user_data;
-    stream->rtcp_interval = (PJMEDIA_RTCP_INTERVAL + (pj_rand() % 8000)) * 
+    stream->rtcp_interval = (PJMEDIA_RTCP_INTERVAL-500 + (pj_rand()%1000)) *
 			    info->fmt.clock_rate / 1000;
 
@@ -1346,4 +1484,14 @@
     stream->rx_event_pt = info->rx_event_pt ? info->rx_event_pt : -1;
     stream->last_dtmf = -1;
+
+    /* Build random RTCP CNAME. CNAME has user@host format */
+    stream->cname.ptr = p = (char*) pj_pool_alloc(pool, 20);
+    pj_create_random_string(p, 5);
+    p += 5;
+    *p++ = '@'; *p++ = 'p'; *p++ = 'j';
+    pj_create_random_string(p, 6);
+    p += 6;
+    *p++ = '.'; *p++ = 'o'; *p++ = 'r'; *p++ = 'g';
+    stream->cname.slen = p - stream->cname.ptr;
 
 
@@ -1610,4 +1758,12 @@
 #endif
 
+    /* Send RTCP SDES */
+    len = create_rtcp_sdes(stream, stream->enc->out_pkt, 
+			   stream->enc->out_pkt_size);
+    if (len != 0) {
+	pjmedia_transport_send_rtcp(stream->transport, 
+				    stream->enc->out_pkt, len);
+    }
+
     /* Success! */
     *p_stream = stream;
@@ -1629,5 +1785,5 @@
 PJ_DEF(pj_status_t) pjmedia_stream_destroy( pjmedia_stream *stream )
 {
-
+    unsigned len;
     PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL);
 
@@ -1669,4 +1825,12 @@
     }
 #endif
+
+    /* Send RTCP BYE */
+    len = create_rtcp_bye(stream, stream->enc->out_pkt, 
+			  stream->enc->out_pkt_size);
+    if (len != 0) {
+	pjmedia_transport_send_rtcp(stream->transport, 
+				    stream->enc->out_pkt, len);
+    }
 
     /* Detach from transport 
