Index: /pjproject/trunk/pjmedia/build/pjmedia.dsp
===================================================================
--- /pjproject/trunk/pjmedia/build/pjmedia.dsp	(revision 213)
+++ /pjproject/trunk/pjmedia/build/pjmedia.dsp	(revision 214)
@@ -66,5 +66,5 @@
 # PROP Target_Dir ""
 # ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c
-# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "../include" /I "../../pjlib/include" /I "../../pjlib-util/include" /I "../src/pjmedia/portaudio" /D "_DEBUG" /D "PA_NO_ASIO" /D "PA_NO_WIN_DS" /D "WIN32" /D "_MBCS" /D "_LIB" /D PJ_WIN32=1 /D PJ_M_I386=1 /FR /FD /GZ /c
+# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "../include" /I "../../pjlib/include" /I "../../pjlib-util/include" /I "../src/pjmedia/portaudio" /D "_DEBUG" /D "PA_NO_ASIO" /D "WIN32" /D "_MBCS" /D "_LIB" /D PJ_WIN32=1 /D PJ_M_I386=1 /FR /FD /GZ /c
 # SUBTRACT CPP /YX
 # ADD BASE RSC /l 0x409 /d "_DEBUG"
Index: /pjproject/trunk/pjmedia/include/pjmedia/errno.h
===================================================================
--- /pjproject/trunk/pjmedia/include/pjmedia/errno.h	(revision 213)
+++ /pjproject/trunk/pjmedia/include/pjmedia/errno.h	(revision 214)
@@ -355,4 +355,9 @@
  */
 #define PJMEDIA_RTP_ENOCONFIG	    (PJMEDIA_ERRNO_START+134)    /* 220134 */
+/**
+ * @hideinitializer
+ * Invalid DTMF digit.
+ */
+#define PJMEDIA_RTP_EINDTMF	    (PJMEDIA_ERRNO_START+135)    /* 220135 */
 
 
Index: /pjproject/trunk/pjmedia/include/pjmedia/rtp.h
===================================================================
--- /pjproject/trunk/pjmedia/include/pjmedia/rtp.h	(revision 213)
+++ /pjproject/trunk/pjmedia/include/pjmedia/rtp.h	(revision 214)
@@ -123,4 +123,24 @@
 
 
+#pragma pack(1)
+
+/**
+ * Declaration for DTMF telephony-events (RFC2833).
+ */
+struct pjmedia_rtp_dtmf_event
+{
+    pj_uint8_t	event;
+    pj_uint8_t	e_vol;
+    pj_uint16_t	duration;
+};
+
+/**
+ * @see pjmedia_rtp_dtmf_event
+ */
+typedef struct pjmedia_rtp_dtmf_event pjmedia_rtp_dtmf_event;
+
+#pragma pack()
+
+
 /**
  * A generic sequence number management, used by both RTP and RTCP.
Index: /pjproject/trunk/pjmedia/include/pjmedia/stream.h
===================================================================
--- /pjproject/trunk/pjmedia/include/pjmedia/stream.h	(revision 213)
+++ /pjproject/trunk/pjmedia/include/pjmedia/stream.h	(revision 214)
@@ -72,4 +72,6 @@
     pj_sockaddr_in	rem_addr;   /**< Remote RTP address		    */
     pjmedia_codec_info	fmt;	    /**< Codec format info.		    */
+    unsigned		tx_event_pt;/**< Outgoing pt for telephone-events.  */
+    unsigned		rx_event_pt;/**< Incoming pt for telephone-events.  */
     pj_uint32_t		ssrc;	    /**< RTP SSRC.			    */
     int			jb_min;	    /**< Jitter buffer min delay.	    */
@@ -184,4 +186,18 @@
 					   pjmedia_dir dir);
 
+/**
+ * Transmit DTMF to this stream. The DTMF will be transmitted uisng
+ * RTP telephone-events as described in RFC 2833. This operation is
+ * only valid for audio stream.
+ *
+ * @param stream	The media stream.
+ * @param digit		A single digit ('0123456789*#ABCD').
+ *
+ * @return		PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjmedia_stream_dial_dtmf(pjmedia_stream *stream,
+					      const pj_str_t *digit_char);
+
+
 
 /**
Index: /pjproject/trunk/pjmedia/src/pjmedia/conference.c
===================================================================
--- /pjproject/trunk/pjmedia/src/pjmedia/conference.c	(revision 213)
+++ /pjproject/trunk/pjmedia/src/pjmedia/conference.c	(revision 214)
@@ -747,4 +747,11 @@
 	    continue;
 
+	//
+	// TODO:
+	//  When there's no source, not transmit the frame, but instead
+	//  transmit a 'silence' frame. This is to allow the 'port' to
+	//  do some processing, such as updating timestamp for RTP session
+	//  or transmit signal when it's in the middle of transmitting DTMF.
+	//
 
 	target_buf = (conf_port->cur_tx_buf==conf_port->tx_buf1?
Index: /pjproject/trunk/pjmedia/src/pjmedia/endpoint.c
===================================================================
--- /pjproject/trunk/pjmedia/src/pjmedia/endpoint.c	(revision 213)
+++ /pjproject/trunk/pjmedia/src/pjmedia/endpoint.c	(revision 214)
@@ -258,9 +258,25 @@
     }
 
-    /* Add sendrect attribute. */
+    /* Add sendrecv attribute. */
     attr = pj_pool_zalloc(pool, sizeof(pjmedia_sdp_attr));
     attr->name = STR_SENDRECV;
     m->attr[m->attr_count++] = attr;
 
+#if 1
+    //
+    // Test: add telephony events
+    //
+    m->desc.fmt[m->desc.fmt_count++] = pj_str("101");
+    /* Add rtpmap. */
+    attr = pj_pool_zalloc(pool, sizeof(pjmedia_sdp_attr));
+    attr->name = pj_str("rtpmap");
+    attr->value = pj_str(":101 telephone-event/8000");
+    m->attr[m->attr_count++] = attr;
+    /* Add fmtp */
+    attr = pj_pool_zalloc(pool, sizeof(pjmedia_sdp_attr));
+    attr->name = pj_str("fmtp");
+    attr->value = pj_str(":101 0-15");
+    m->attr[m->attr_count++] = attr;
+#endif
 
     /* Done */
Index: /pjproject/trunk/pjmedia/src/pjmedia/stream.c
===================================================================
--- /pjproject/trunk/pjmedia/src/pjmedia/stream.c	(revision 213)
+++ /pjproject/trunk/pjmedia/src/pjmedia/stream.c	(revision 214)
@@ -23,4 +23,5 @@
 #include <pjmedia/jbuf.h>
 #include <pj/os.h>
+#include <pj/ctype.h>
 #include <pj/log.h>
 #include <pj/string.h>	    /* memcpy() */
@@ -40,5 +41,5 @@
 #define PJMEDIA_MAX_BUFFER_SIZE_MS	2000
 #define PJMEDIA_MAX_MTU			1500
-
+#define PJMEDIA_DTMF_DURATION		1600	/* in timestamp */
 
 
@@ -64,4 +65,11 @@
 
 
+struct dtmf
+{
+    int		    event;
+    pj_uint32_t	    start_ts;
+    pj_uint32_t	    end_ts;
+};
+
 /**
  * This structure describes media stream.
@@ -94,4 +102,8 @@
     pj_bool_t		     quit_flag;	    /**< To signal thread exit.	    */
     pj_thread_t		    *thread;	    /**< Jitter buffer's thread.    */
+
+    /* RFC 2833 DTMF transmission queue: */
+    int			     dtmf_count;    /**< # of digits in queue.	    */
+    struct dtmf		     dtmf_queue[32];/**< Outgoing dtmf queue.	    */
 };
 
@@ -179,4 +191,22 @@
 
 
+/*
+ * Transmit DTMF
+ */
+static void transmit_dtmf(pjmedia_stream *stream, 
+			  struct pjmedia_frame *frame_out)
+{
+    pjmedia_rtp_dtmf_event *event;
+    struct dtmf *digit = &stream->dtmf_queue[0];
+    pj_uint32_t cur_ts;
+
+    event = frame_out->buf;
+    cur_ts = pj_ntohl(stream->enc->rtp.out_hdr.ts);
+
+    event->event = (pj_uint8_t)digit->event;
+    event->e_vol = 10;
+    event->duration = pj_htonl(cur_ts - digit->start_ts);
+}
+
 /**
  * rec_callback()
@@ -203,16 +233,29 @@
 	return -1;
 
-    /* Encode. */
+    /* Number of samples in the frame */
+    ts_len = frame->size / (channel->snd_info.bits_per_sample / 8);
+
+    /* Init frame_out buffer. */
     frame_out.buf = ((char*)channel->out_pkt) + sizeof(pjmedia_rtp_hdr);
-    status = stream->codec->op->encode( stream->codec, frame, 
-					channel->out_pkt_size - sizeof(pjmedia_rtp_hdr), 
-					&frame_out);
-    if (status != 0) {
-	TRACE_((THIS_FILE, "Codec encode() error", status));
-	return status;
+
+    /* If we have DTMF digits in the queue, transmit the digits. 
+     * Otherwise encode the PCM buffer.
+     */
+    if (stream->dtmf_count) {
+	transmit_dtmf(stream, &frame_out);
+    } else {
+	unsigned max_size;
+
+	max_size = channel->out_pkt_size - sizeof(pjmedia_rtp_hdr);
+	status = stream->codec->op->encode( stream->codec, frame, 
+					    max_size, 
+					    &frame_out);
+	if (status != 0) {
+	    TRACE_((THIS_FILE, "Codec encode() error", status));
+	    return status;
+	}
     }
 
     /* Encapsulate. */
-    ts_len = frame->size / (channel->snd_info.bits_per_sample / 8);
     status = pjmedia_rtp_encode_rtp( &channel->rtp, 
 				channel->pt, 0, 
@@ -247,4 +290,27 @@
 }
 
+
+static void dump_bin(const char *buf, unsigned len)
+{
+    unsigned i;
+
+    PJ_LOG(3,(THIS_FILE, "begin dump"));
+    for (i=0; i<len; ++i) {
+	int j;
+	char bits[9];
+	unsigned val = buf[i] & 0xFF;
+
+	bits[8] = '\0';
+	for (j=0; j<8; ++j) {
+	    if (val & (1 << (7-j)))
+		bits[j] = '1';
+	    else
+		bits[j] = '0';
+	}
+
+	PJ_LOG(3,(THIS_FILE, "%2d %s [%d]", i, bits, val));
+    }
+    PJ_LOG(3,(THIS_FILE, "end dump"));
+}
 
 /*
@@ -311,4 +377,11 @@
 	}
 
+#if 1
+	if (hdr->pt == 101) {
+	    dump_bin((char*)payload, payloadlen);
+	    continue;
+	}
+#endif
+
 	status = pjmedia_rtp_session_update(&channel->rtp, hdr);
 	if (status != 0 && 
@@ -739,2 +812,84 @@
 }
 
+/*
+ * Dial DTMF
+ */
+PJ_DEF(pj_status_t) pjmedia_stream_dial_dtmf( pjmedia_stream *stream,
+					      const pj_str_t *digit_char)
+{
+    pj_status_t status = PJ_SUCCESS;
+
+    /* By convention we use jitter buffer mutex to access DTMF
+     * queue.
+     */
+    PJ_ASSERT_RETURN(stream && digit_char, PJ_EINVAL);
+
+    pj_mutex_lock(stream->jb_mutex);
+    
+    if (stream->dtmf_count+digit_char->slen >=
+	PJ_ARRAY_SIZE(stream->dtmf_queue))
+    {
+	status = PJ_ETOOMANY;
+    } else {
+	int i;
+
+	/* convert ASCII digits into payload type first, to make sure
+	 * that all digits are valid. 
+	 */
+	for (i=0; i<digit_char->slen; ++i) {
+	    unsigned pt;
+
+	    if (digit_char->ptr[i] >= '0' &&
+		digit_char->ptr[i] <= '9')
+	    {
+		pt = digit_char->ptr[i] - '0';
+	    } 
+	    else if (pj_tolower(digit_char->ptr[i]) >= 'a' &&
+		     pj_tolower(digit_char->ptr[i]) <= 'd')
+	    {
+		pt = pj_tolower(digit_char->ptr[i]) - 'a' + 12;
+	    }
+	    else if (digit_char->ptr[i] == '*')
+	    {
+		pt = 10;
+	    }
+	    else if (digit_char->ptr[i] == '#')
+	    {
+		pt = 11;
+	    }
+	    else
+	    {
+		status = PJMEDIA_RTP_EINDTMF;
+		break;
+	    }
+
+	    stream->dtmf_queue[stream->dtmf_count+1].event = pt;
+	    stream->dtmf_queue[stream->dtmf_count+1].start_ts = start_ts;
+	    stream->dtmf_queue[stream->dtmf_count+1].end_ts = 
+		start_ts + PJMEDIA_DTMF_DURATION;
+
+	    start_ts += PJMEDIA_DTMF_DURATION + 320;
+	}
+
+	if (status != PJ_SUCCESS)
+	    goto on_return;
+
+	if (stream->dtmf_count ==0) {
+	    pj_uint32_t start_ts;
+
+	    start_ts = pj_ntohl(stream->enc->rtp.out_hdr.ts);
+	    stream->dtmf_queue[0].start_ts = start_ts;
+	    stream->dtmf_queue[0].end_ts = start_ts + PJMEDIA_DTMF_DURATION;
+	}
+
+	/* Increment digit count only if all digits are valid. */
+	stream->dtmf_count += digit_char->slen;
+
+    }
+
+on_return:
+    pj_mutex_unlock(stream->jb_mutex);
+
+    return status;
+}
+
