Index: /pjproject/trunk/pjmedia/include/pjmedia/config.h
===================================================================
--- /pjproject/trunk/pjmedia/include/pjmedia/config.h	(revision 2758)
+++ /pjproject/trunk/pjmedia/include/pjmedia/config.h	(revision 2759)
@@ -697,4 +697,66 @@
 
 
+/**
+ * Value to be specified in PJMEDIA_STREAM_ENABLE_KA setting.
+ * This indicates that an empty RTP packet should be used as
+ * the keep-alive packet.
+ */
+#define PJMEDIA_STREAM_KA_EMPTY_RTP		    1
+
+/**
+ * Value to be specified in PJMEDIA_STREAM_ENABLE_KA setting.
+ * This indicates that a user defined packet should be used
+ * as the keep-alive packet. The content of the user-defined
+ * packet is specified by PJMEDIA_STREAM_KA_USER_PKT. Default
+ * content is a CR-LF packet.
+ */
+#define PJMEDIA_STREAM_KA_USER			    2
+
+/**
+ * The content of the user defined keep-alive packet. The format
+ * of the packet is initializer to pj_str_t structure. Note that
+ * the content may contain NULL character.
+ */
+#ifndef PJMEDIA_STREAM_KA_USER_PKT
+#   define PJMEDIA_STREAM_KA_USER_PKT	{ "\r\n", 2 }
+#endif
+
+/**
+ * Specify another type of keep-alive and NAT hole punching 
+ * mechanism (the other type is PJMEDIA_STREAM_VAD_SUSPEND_MSEC
+ * and PJMEDIA_CODEC_MAX_SILENCE_PERIOD) to be used by stream. 
+ * When this feature is enabled, the stream will initially 
+ * transmit one packet to punch a hole in NAT, and periodically
+ * transmit keep-alive packets.
+ *
+ * When this alternative keep-alive mechanism is used, application
+ * may disable the other keep-alive mechanisms, i.e: by setting 
+ * PJMEDIA_STREAM_VAD_SUSPEND_MSEC to zero and 
+ * PJMEDIA_CODEC_MAX_SILENCE_PERIOD to -1.
+ *
+ * The value of this macro specifies the type of packet used
+ * for the keep-alive mechanism. Valid values are
+ * PJMEDIA_STREAM_KA_EMPTY_RTP and PJMEDIA_STREAM_KA_USER.
+ * 
+ * The duration of the keep-alive interval further can be set
+ * with PJMEDIA_STREAM_KA_INTERVAL setting.
+ *
+ * Default: 0 (disabled)
+ */
+#ifndef PJMEDIA_STREAM_ENABLE_KA
+#   define PJMEDIA_STREAM_ENABLE_KA		    0
+#endif
+
+
+/**
+ * Specify the keep-alive interval of PJMEDIA_STREAM_ENABLE_KA
+ * mechanism, in seconds.
+ *
+ * Default: 5 seconds
+ */
+#ifndef PJMEDIA_STREAM_KA_INTERVAL
+#	define PJMEDIA_STREAM_KA_INTERVAL	    5
+#endif
+
 
 /**
Index: /pjproject/trunk/pjmedia/src/pjmedia/stream.c
===================================================================
--- /pjproject/trunk/pjmedia/src/pjmedia/stream.c	(revision 2758)
+++ /pjproject/trunk/pjmedia/src/pjmedia/stream.c	(revision 2759)
@@ -169,4 +169,9 @@
 					            address		    */
 #endif
+
+#if defined(PJMEDIA_STREAM_ENABLE_KA) && PJMEDIA_STREAM_ENABLE_KA!=0
+    pj_timestamp	     last_frm_ts_sent; /**< Timestamp of last sending
+					            packet		    */
+#endif
 };
 
@@ -193,4 +198,47 @@
 }
 
+/*
+ * Send keep-alive packet.
+ */
+static void send_keep_alive_packet(pjmedia_stream *stream)
+{
+#if defined(PJMEDIA_STREAM_ENABLE_KA) && \
+    PJMEDIA_STREAM_ENABLE_KA == PJMEDIA_STREAM_KA_EMPTY_RTP
+
+    /* Keep-alive packet is empty RTP */
+    pj_status_t status;
+    int pkt_len;
+
+
+    status = pjmedia_rtp_encode_rtp( &stream->enc->rtp,
+				     stream->enc->pt, 1,
+				     1,
+				     0,
+				     (const void**)stream->enc->out_pkt,
+				     &pkt_len);
+    pj_assert(status == PJ_SUCCESS);
+    pjmedia_transport_send_rtp(stream->transport, stream->enc->out_pkt,
+			       pkt_len);
+    TRC_((stream->port.info.name.ptr, "Keep-alive sent (empty RTP)"));
+
+#elif defined(PJMEDIA_STREAM_ENABLE_KA) && \
+      PJMEDIA_STREAM_ENABLE_KA == PJMEDIA_STREAM_KA_USER
+
+    /* Keep-alive packet is defined in PJMEDIA_STREAM_KA_USER_PKT */
+    int pkt_len;
+    const pj_str_t str_ka = PJMEDIA_STREAM_KA_USER_PKT;
+
+    pj_memcpy(stream->enc->out_pkt, str_ka.ptr, str_ka.slen);
+    pkt_len = str_ka.slen;
+    pjmedia_transport_send_rtp(stream->transport, stream->enc->out_pkt,
+			       pkt_len);
+    TRC_((stream->port.info.name.ptr, "Keep-alive sent"));
+
+#else
+    
+    PJ_UNUSED_ARG(stream);
+
+#endif
+}
 
 /*
@@ -787,4 +835,23 @@
     int rtphdrlen;
     int inc_timestamp = 0;
+
+
+#if defined(PJMEDIA_STREAM_ENABLE_KA) && PJMEDIA_STREAM_ENABLE_KA != 0
+    /* If the interval since last sending packet is greater than
+     * PJMEDIA_STREAM_KA_INTERVAL, send keep-alive packet.
+     */
+    {
+	pj_uint32_t dtx_duration;
+
+	dtx_duration = pj_timestamp_diff32(&stream->last_frm_ts_sent, 
+					   &frame->timestamp);
+	if (dtx_duration >
+	    PJMEDIA_STREAM_KA_INTERVAL * stream->port.info.clock_rate)
+	{
+	    send_keep_alive_packet(stream);
+	    stream->last_frm_ts_sent = frame->timestamp;
+	}
+    }
+#endif
 
     /* Don't do anything if stream is paused */
@@ -991,4 +1058,9 @@
     stream->rtcp.stat.rtp_tx_last_seq = pj_ntohs(stream->enc->rtp.out_hdr.seq);
 
+#if defined(PJMEDIA_STREAM_ENABLE_KA) && PJMEDIA_STREAM_ENABLE_KA!=0
+    /* Update timestamp of last sending packet. */
+    stream->last_frm_ts_sent = frame->timestamp;
+#endif
+
     return PJ_SUCCESS;
 }
@@ -1910,4 +1982,9 @@
 				    stream->enc->out_pkt, len);
     }
+
+#if defined(PJMEDIA_STREAM_ENABLE_KA) && PJMEDIA_STREAM_ENABLE_KA!=0
+    /* NAT hole punching by sending KA packet via RTP transport. */
+    send_keep_alive_packet(stream);
+#endif
 
     /* Success! */
