From c6977b9983db4f58446bfbc65a5b028cda8244ee Mon Sep 17 00:00:00 2001
From: Joshua Colp <jcolp@digium.com>
Date: Thu, 31 Aug 2006 01:59:02 +0000
Subject: [PATCH] Merge in VLDTMF support with Zaptel/Core done by the ever
 great Darumkilla Russell Bryant and the RTP portion done by myself,
 Muffinlicious Joshua Colp. This has gone through so many
 discussions/revisions it's not funny but we finally have it!

git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@41507 65c4cc65-6c06-0410-ace0-fbb531ad65f3
---
 UPGRADE.txt                |   6 +
 apps/app_echo.c            |   8 +-
 channels/chan_agent.c      |  30 +++--
 channels/chan_alsa.c       |   2 +-
 channels/chan_features.c   |  26 +++-
 channels/chan_h323.c       |  14 +-
 channels/chan_iax2.c       |  15 ++-
 channels/chan_jingle.c     |  14 +-
 channels/chan_local.c      |  27 +++-
 channels/chan_mgcp.c       |  16 ++-
 channels/chan_misdn.c      |  14 +-
 channels/chan_oss.c        |  13 +-
 channels/chan_phone.c      |  19 ++-
 channels/chan_sip.c        |  46 ++++++-
 channels/chan_skinny.c     |  15 ++-
 channels/chan_vpb.cc       |  55 ++++----
 channels/chan_zap.c        | 155 ++++++++++++++-------
 channels/iax2-parser.c     |   9 +-
 configs/sip.conf.sample    |   9 +-
 include/asterisk/channel.h |  40 +++---
 include/asterisk/frame.h   |  66 ++++-----
 include/asterisk/rtp.h     |   7 +-
 main/app.c                 |  11 +-
 main/channel.c             | 133 +++++++++++++-----
 main/dsp.c                 |  63 ++++-----
 main/file.c                |   7 +-
 main/rtp.c                 | 269 +++++++++++++++++++++++--------------
 res/res_agi.c              |   2 +
 res/res_features.c         |   6 +-
 29 files changed, 738 insertions(+), 359 deletions(-)

diff --git a/UPGRADE.txt b/UPGRADE.txt
index 9a75748a87..f0e7246b70 100644
--- a/UPGRADE.txt
+++ b/UPGRADE.txt
@@ -303,6 +303,12 @@ The SIP channel:
   option in sip.conf is removed to osp.conf as authpolicy. allowguest option
   in sip.conf cannot be set as osp anymore. 
 
+* The Asterisk RTP stack has been changed in regards to RFC2833 reception
+  and transmission. Packets will now be sent with proper duration instead of all
+  at once. If you are receiving calls from a pre-1.4 Asterisk installation you
+  will want to turn on the rfc2833compensate option. Without this option your
+  DTMF reception may act poorly.
+
 * The $SIPUSERAGENT dialplan variable is deprecated and will be removed
   in coming versions of Asterisk. Please use the dialplan function
   SIPCHANINFO(useragent) instead.
diff --git a/apps/app_echo.c b/apps/app_echo.c
index 19a86382ec..4b2c940562 100644
--- a/apps/app_echo.c
+++ b/apps/app_echo.c
@@ -71,19 +71,13 @@ static int echo_exec(struct ast_channel *chan, void *data)
 		f->delivery.tv_usec = 0;
 		switch (f->frametype) {
 		case AST_FRAME_DTMF:
-		case AST_FRAME_DTMF_END:
 			if (f->subclass == '#') {
 				res = 0;
 				ast_frfree(f);
 				goto end;
 			}
 			/* fall through */
-		case AST_FRAME_DTMF_BEGIN:
-		case AST_FRAME_VOICE:
-		case AST_FRAME_VIDEO:
-		case AST_FRAME_TEXT:
-		case AST_FRAME_HTML:
-		case AST_FRAME_IMAGE:
+		default:
 			if (ast_write(chan, f)) {
 				ast_frfree(f);
 				goto end;
diff --git a/channels/chan_agent.c b/channels/chan_agent.c
index 0a9b8906ac..4eeffe99c8 100644
--- a/channels/chan_agent.c
+++ b/channels/chan_agent.c
@@ -238,7 +238,8 @@ static AST_LIST_HEAD_STATIC(agents, agent_pvt);	/*!< Holds the list of agents (l
 static struct ast_channel *agent_request(const char *type, int format, void *data, int *cause);
 static int agent_devicestate(void *data);
 static void agent_logoff_maintenance(struct agent_pvt *p, char *loginchan, long logintime, const char *uniqueid, char *logcommand);
-static int agent_digit(struct ast_channel *ast, char digit);
+static int agent_digit_begin(struct ast_channel *ast, char digit);
+static int agent_digit_end(struct ast_channel *ast, char digit);
 static int agent_call(struct ast_channel *ast, char *dest, int timeout);
 static int agent_hangup(struct ast_channel *ast);
 static int agent_answer(struct ast_channel *ast);
@@ -258,7 +259,8 @@ static const struct ast_channel_tech agent_tech = {
 	.capabilities = -1,
 	.requester = agent_request,
 	.devicestate = agent_devicestate,
-	.send_digit = agent_digit,
+	.send_digit_begin = agent_digit_begin,
+	.send_digit_end = agent_digit_end,
 	.call = agent_call,
 	.hangup = agent_hangup,
 	.answer = agent_answer,
@@ -491,7 +493,8 @@ static struct ast_frame *agent_read(struct ast_channel *ast)
  				}
  			}
  			break;
- 		case AST_FRAME_DTMF:
+		case AST_FRAME_DTMF_BEGIN:
+ 		case AST_FRAME_DTMF_END:
  			if (!p->acknowledged && (f->subclass == '#')) {
  				if (option_verbose > 2)
  					ast_verbose(VERBOSE_PREFIX_3 "%s acknowledged\n", p->chan->name);
@@ -511,7 +514,9 @@ static struct ast_frame *agent_read(struct ast_channel *ast)
  				ast_frfree(f);
  				f = &ast_null_frame;
  			}
- 			break;
+		default:
+			/* pass everything else on through */
+			break;
   		}
   	}
 
@@ -603,15 +608,22 @@ static int agent_indicate(struct ast_channel *ast, int condition, const void *da
 	return res;
 }
 
-static int agent_digit(struct ast_channel *ast, char digit)
+static int agent_digit_begin(struct ast_channel *ast, char digit)
 {
 	struct agent_pvt *p = ast->tech_pvt;
 	int res = -1;
 	ast_mutex_lock(&p->lock);
-	if (p->chan)
-		res = p->chan->tech->send_digit(p->chan, digit);
-	else
-		res = 0;
+	ast_senddigit_begin(p->chan, digit);
+	ast_mutex_unlock(&p->lock);
+	return res;
+}
+
+static int agent_digit_end(struct ast_channel *ast, char digit)
+{
+	struct agent_pvt *p = ast->tech_pvt;
+	int res = -1;
+	ast_mutex_lock(&p->lock);
+	ast_senddigit_end(p->chan, digit);
 	ast_mutex_unlock(&p->lock);
 	return res;
 }
diff --git a/channels/chan_alsa.c b/channels/chan_alsa.c
index 5a4d6276f6..4237ba800e 100644
--- a/channels/chan_alsa.c
+++ b/channels/chan_alsa.c
@@ -203,7 +203,7 @@ static const struct ast_channel_tech alsa_tech = {
 	.description = tdesc,
 	.capabilities = AST_FORMAT_SLINEAR,
 	.requester = alsa_request,
-	.send_digit = alsa_digit,
+	.send_digit_end = alsa_digit,
 	.send_text = alsa_text,
 	.hangup = alsa_hangup,
 	.answer = alsa_answer,
diff --git a/channels/chan_features.c b/channels/chan_features.c
index d1feadbea9..66b6da3e37 100644
--- a/channels/chan_features.c
+++ b/channels/chan_features.c
@@ -95,7 +95,8 @@ static AST_LIST_HEAD_STATIC(features, feature_pvt);
 #define SUB_THREEWAY	2			/* Three-way call */
 
 static struct ast_channel *features_request(const char *type, int format, void *data, int *cause);
-static int features_digit(struct ast_channel *ast, char digit);
+static int features_digit_begin(struct ast_channel *ast, char digit);
+static int features_digit_end(struct ast_channel *ast, char digit);
 static int features_call(struct ast_channel *ast, char *dest, int timeout);
 static int features_hangup(struct ast_channel *ast);
 static int features_answer(struct ast_channel *ast);
@@ -109,7 +110,8 @@ static const struct ast_channel_tech features_tech = {
 	.description = tdesc,
 	.capabilities = -1,
 	.requester = features_request,
-	.send_digit = features_digit,
+	.send_digit_begin = features_digit_begin,
+	.send_digit_end = features_digit_end,
 	.call = features_call,
 	.hangup = features_hangup,
 	.answer = features_answer,
@@ -300,7 +302,7 @@ static int features_indicate(struct ast_channel *ast, int condition, const void
 	return res;
 }
 
-static int features_digit(struct ast_channel *ast, char digit)
+static int features_digit_begin(struct ast_channel *ast, char digit)
 {
 	struct feature_pvt *p = ast->tech_pvt;
 	int res = -1;
@@ -310,7 +312,23 @@ static int features_digit(struct ast_channel *ast, char digit)
 	ast_mutex_lock(&p->lock);
 	x = indexof(p, ast, 0);
 	if (!x && p->subchan)
-		res = ast_senddigit(p->subchan, digit);
+		res = ast_senddigit_begin(p->subchan, digit);
+	ast_mutex_unlock(&p->lock);
+
+	return res;
+}
+
+static int features_digit_end(struct ast_channel *ast, char digit)
+{
+	struct feature_pvt *p = ast->tech_pvt;
+	int res = -1;
+	int x;
+
+	/* Queue up a frame representing the indication as a control frame */
+	ast_mutex_lock(&p->lock);
+	x = indexof(p, ast, 0);
+	if (!x && p->subchan)
+		res = ast_senddigit_end(p->subchan, digit);
 	ast_mutex_unlock(&p->lock);
 	return res;
 }
diff --git a/channels/chan_h323.c b/channels/chan_h323.c
index 6b956e517a..dc498a2d9d 100644
--- a/channels/chan_h323.c
+++ b/channels/chan_h323.c
@@ -220,7 +220,8 @@ static int restart_monitor(void);
 static int h323_do_reload(void);
 
 static struct ast_channel *oh323_request(const char *type, int format, void *data, int *cause);
-static int oh323_digit(struct ast_channel *c, char digit);
+static int oh323_digit_begin(struct ast_channel *c, char digit);
+static int oh323_digit_end(struct ast_channel *c, char digit);
 static int oh323_call(struct ast_channel *c, char *dest, int timeout);
 static int oh323_hangup(struct ast_channel *c);
 static int oh323_answer(struct ast_channel *c);
@@ -235,7 +236,8 @@ static const struct ast_channel_tech oh323_tech = {
 	.capabilities = ((AST_FORMAT_MAX_AUDIO << 1) - 1),
 	.properties = AST_CHAN_TP_WANTSJITTER | AST_CHAN_TP_CREATESJITTER,
 	.requester = oh323_request,
-	.send_digit = oh323_digit,
+	.send_digit_begin = oh323_digit_begin,
+	.send_digit_end = oh323_digit_end,
 	.call = oh323_call,
 	.hangup = oh323_hangup,
 	.answer = oh323_answer,
@@ -381,11 +383,17 @@ static void oh323_destroy(struct oh323_pvt *pvt)
 	ast_mutex_unlock(&iflock);
 }
 
+static int oh323_digit_begin(struct ast_channel *chan, char digit)
+{
+	/* XXX Implement me, plz, kthx */
+	return 0;
+}
+
 /**
  * Send (play) the specified digit to the channel.
  * 
  */
-static int oh323_digit(struct ast_channel *c, char digit)
+static int oh323_digit_end(struct ast_channel *c, char digit)
 {
 	struct oh323_pvt *pvt = (struct oh323_pvt *) c->tech_pvt;
 	char *token;
diff --git a/channels/chan_iax2.c b/channels/chan_iax2.c
index 1d404c3fbf..60841e0ae4 100644
--- a/channels/chan_iax2.c
+++ b/channels/chan_iax2.c
@@ -776,7 +776,8 @@ static int expire_registry(void *data);
 static int iax2_answer(struct ast_channel *c);
 static int iax2_call(struct ast_channel *c, char *dest, int timeout);
 static int iax2_devicestate(void *data);
-static int iax2_digit(struct ast_channel *c, char digit);
+static int iax2_digit_begin(struct ast_channel *c, char digit);
+static int iax2_digit_end(struct ast_channel *c, char digit);
 static int iax2_do_register(struct iax2_registry *reg);
 static int iax2_fixup(struct ast_channel *oldchannel, struct ast_channel *newchan);
 static int iax2_hangup(struct ast_channel *c);
@@ -809,7 +810,8 @@ static const struct ast_channel_tech iax2_tech = {
 	.properties = AST_CHAN_TP_WANTSJITTER,
 	.requester = iax2_request,
 	.devicestate = iax2_devicestate,
-	.send_digit = iax2_digit,
+	.send_digit_begin = iax2_digit_begin,
+	.send_digit_end = iax2_digit_end,
 	.send_text = iax2_sendtext,
 	.send_image = iax2_sendimage,
 	.send_html = iax2_sendhtml,
@@ -2379,9 +2381,14 @@ static int iax2_transmit(struct iax_frame *fr)
 
 
 
-static int iax2_digit(struct ast_channel *c, char digit)
+static int iax2_digit_begin(struct ast_channel *c, char digit)
 {
-	return send_command_locked(PTR_TO_CALLNO(c->tech_pvt), AST_FRAME_DTMF, digit, 0, NULL, 0, -1);
+	return send_command_locked(PTR_TO_CALLNO(c->tech_pvt), AST_FRAME_DTMF_BEGIN, digit, 0, NULL, 0, -1);
+}
+
+static int iax2_digit_end(struct ast_channel *c, char digit)
+{
+	return send_command_locked(PTR_TO_CALLNO(c->tech_pvt), AST_FRAME_DTMF_END, digit, 0, NULL, 0, -1);
 }
 
 static int iax2_sendtext(struct ast_channel *c, const char *text)
diff --git a/channels/chan_jingle.c b/channels/chan_jingle.c
index b3d7ea68a1..a44b42085b 100644
--- a/channels/chan_jingle.c
+++ b/channels/chan_jingle.c
@@ -171,7 +171,8 @@ AST_MUTEX_DEFINE_STATIC(jinglelock); /*!< Protect the interface list (of jingle_
 
 /* Forward declarations */
 static struct ast_channel *jingle_request(const char *type, int format, void *data, int *cause);
-static int jingle_digit(struct ast_channel *ast, char digit);
+static int jingle_digit_begin(struct ast_channel *ast, char digit);
+static int jingle_digit_end(struct ast_channel *ast, char digit);
 static int jingle_call(struct ast_channel *ast, char *dest, int timeout);
 static int jingle_hangup(struct ast_channel *ast);
 static int jingle_answer(struct ast_channel *ast);
@@ -194,7 +195,8 @@ static const struct ast_channel_tech jingle_tech = {
 	.description = "Jingle Channel Driver",
 	.capabilities = ((AST_FORMAT_MAX_AUDIO << 1) - 1),
 	.requester = jingle_request,
-	.send_digit = jingle_digit,
+	.send_digit_begin = jingle_digit_begin,
+	.send_digit_end = jingle_digit_end,
 	.bridge = ast_rtp_bridge,
 	.call = jingle_call,
 	.hangup = jingle_hangup,
@@ -1181,7 +1183,13 @@ static int jingle_indicate(struct ast_channel *ast, int condition, const void *d
 	return res;
 }
 
-static int jingle_digit(struct ast_channel *ast, char digit)
+static int jingle_digit_begin(struct ast_channel *chan, char digit)
+{
+	/* XXX Does jingle have a concept of the length of a dtmf digit ? */
+	return 0;
+}
+
+static int jingle_digit_end(struct ast_channel *ast, char digit)
 {
 	struct jingle_pvt *p = ast->tech_pvt;
 	struct jingle *client = p->parent;
diff --git a/channels/chan_local.c b/channels/chan_local.c
index a9b0582f9b..fec9c6273f 100644
--- a/channels/chan_local.c
+++ b/channels/chan_local.c
@@ -67,7 +67,8 @@ static const char tdesc[] = "Local Proxy Channel Driver";
 #define IS_OUTBOUND(a,b) (a == b->chan ? 1 : 0)
 
 static struct ast_channel *local_request(const char *type, int format, void *data, int *cause);
-static int local_digit(struct ast_channel *ast, char digit);
+static int local_digit_begin(struct ast_channel *ast, char digit);
+static int local_digit_end(struct ast_channel *ast, char digit);
 static int local_call(struct ast_channel *ast, char *dest, int timeout);
 static int local_hangup(struct ast_channel *ast);
 static int local_answer(struct ast_channel *ast);
@@ -85,7 +86,8 @@ static const struct ast_channel_tech local_tech = {
 	.description = tdesc,
 	.capabilities = -1,
 	.requester = local_request,
-	.send_digit = local_digit,
+	.send_digit_begin = local_digit_begin,
+	.send_digit_end = local_digit_end,
 	.call = local_call,
 	.hangup = local_hangup,
 	.answer = local_answer,
@@ -327,11 +329,11 @@ static int local_indicate(struct ast_channel *ast, int condition, const void *da
 	return res;
 }
 
-static int local_digit(struct ast_channel *ast, char digit)
+static int local_digit_begin(struct ast_channel *ast, char digit)
 {
 	struct local_pvt *p = ast->tech_pvt;
 	int res = -1;
-	struct ast_frame f = { AST_FRAME_DTMF, };
+	struct ast_frame f = { AST_FRAME_DTMF_BEGIN, };
 	int isoutbound;
 
 	ast_mutex_lock(&p->lock);
@@ -339,6 +341,23 @@ static int local_digit(struct ast_channel *ast, char digit)
 	f.subclass = digit;
 	res = local_queue_frame(p, isoutbound, &f, ast);
 	ast_mutex_unlock(&p->lock);
+
+	return res;
+}
+
+static int local_digit_end(struct ast_channel *ast, char digit)
+{
+	struct local_pvt *p = ast->tech_pvt;
+	int res = -1;
+	struct ast_frame f = { AST_FRAME_DTMF_END, };
+	int isoutbound;
+
+	ast_mutex_lock(&p->lock);
+	isoutbound = IS_OUTBOUND(ast, p);
+	f.subclass = digit;
+	res = local_queue_frame(p, isoutbound, &f, ast);
+	ast_mutex_unlock(&p->lock);
+	
 	return res;
 }
 
diff --git a/channels/chan_mgcp.c b/channels/chan_mgcp.c
index c77b8a61e5..05ab81edfd 100644
--- a/channels/chan_mgcp.c
+++ b/channels/chan_mgcp.c
@@ -427,7 +427,8 @@ static struct ast_frame *mgcp_read(struct ast_channel *ast);
 static int mgcp_write(struct ast_channel *ast, struct ast_frame *frame);
 static int mgcp_indicate(struct ast_channel *ast, int ind, const void *data, size_t datalen);
 static int mgcp_fixup(struct ast_channel *oldchan, struct ast_channel *newchan);
-static int mgcp_senddigit(struct ast_channel *ast, char digit);
+static int mgcp_senddigit_begin(struct ast_channel *ast, char digit);
+static int mgcp_senddigit_end(struct ast_channel *ast, char digit);
 static int mgcp_devicestate(void *data);
 
 static const struct ast_channel_tech mgcp_tech = {
@@ -444,7 +445,8 @@ static const struct ast_channel_tech mgcp_tech = {
 	.write = mgcp_write,
 	.indicate = mgcp_indicate,
 	.fixup = mgcp_fixup,
-	.send_digit = mgcp_senddigit,
+	.send_digit_begin = mgcp_senddigit_begin,
+	.send_digit_end = mgcp_senddigit_end,
 	.bridge = ast_rtp_bridge,
 };
 
@@ -1221,7 +1223,13 @@ static int mgcp_fixup(struct ast_channel *oldchan, struct ast_channel *newchan)
 	return 0;
 }
 
-static int mgcp_senddigit(struct ast_channel *ast, char digit)
+static int mgcp_senddigit_begin(struct ast_channel *ast, char digit)
+{
+	/* Let asterisk play inband indications */
+	return -1;
+}
+
+static int mgcp_senddigit_end(struct ast_channel *ast, char digit)
 {
 	struct mgcp_subchannel *sub = ast->tech_pvt;
 	char tmp[4];
@@ -1233,7 +1241,7 @@ static int mgcp_senddigit(struct ast_channel *ast, char digit)
 	ast_mutex_lock(&sub->lock);
 	transmit_notify_request(sub, tmp);
 	ast_mutex_unlock(&sub->lock);
-	return -1;
+	return -1; /* Return non-zero so that Asterisk will stop the inband indications */
 }
 
 /*!
diff --git a/channels/chan_misdn.c b/channels/chan_misdn.c
index 9631a8cb53..201d38c39c 100644
--- a/channels/chan_misdn.c
+++ b/channels/chan_misdn.c
@@ -2006,7 +2006,13 @@ static int misdn_answer(struct ast_channel *ast)
 	return 0;
 }
 
-static int misdn_digit(struct ast_channel *ast, char digit )
+static int misdn_digit_begin(struct ast_channel *chan, char digit)
+{
+	/* XXX Modify this callback to support Asterisk controlling the length of DTMF */
+	return 0;
+}
+
+static int misdn_digit_end(struct ast_channel *ast, char digit )
 {
 	struct chan_list *p;
 	
@@ -2983,7 +2989,8 @@ static struct ast_channel_tech misdn_tech = {
 	.description="Channel driver for mISDN Support (Bri/Pri)",
 	.capabilities= AST_FORMAT_ALAW ,
 	.requester=misdn_request,
-	.send_digit=misdn_digit,
+	.send_digit_begin=misdn_digit_begin,
+	.send_digit_end=misdn_digit_end,
 	.call=misdn_call,
 	.bridge=misdn_bridge, 
 	.hangup=misdn_hangup,
@@ -3001,7 +3008,8 @@ static struct ast_channel_tech misdn_tech_wo_bridge = {
 	.description="Channel driver for mISDN Support (Bri/Pri)",
 	.capabilities=AST_FORMAT_ALAW ,
 	.requester=misdn_request,
-	.send_digit=misdn_digit,
+	.send_digit_begin=misdn_digit_begin,
+	.send_digit_end=misdn_digit_end,
 	.call=misdn_call,
 	.hangup=misdn_hangup,
 	.answer=misdn_answer,
diff --git a/channels/chan_oss.c b/channels/chan_oss.c
index b2981463c6..e40aacb0f2 100644
--- a/channels/chan_oss.c
+++ b/channels/chan_oss.c
@@ -407,7 +407,8 @@ static int setformat(struct chan_oss_pvt *o, int mode);
 
 static struct ast_channel *oss_request(const char *type, int format, void *data
 , int *cause);
-static int oss_digit(struct ast_channel *c, char digit);
+static int oss_digit_begin(struct ast_channel *c, char digit);
+static int oss_digit_end(struct ast_channel *c, char digit);
 static int oss_text(struct ast_channel *c, const char *text);
 static int oss_hangup(struct ast_channel *c);
 static int oss_answer(struct ast_channel *c);
@@ -423,7 +424,8 @@ static const struct ast_channel_tech oss_tech = {
 	.description =	tdesc,
 	.capabilities =	AST_FORMAT_SLINEAR,
 	.requester = oss_request,
-	.send_digit = oss_digit,
+	.send_digit_begin = oss_digit_begin,
+	.send_digit_end = oss_digit_end,
 	.send_text = oss_text,
 	.hangup = oss_hangup,
 	.answer = oss_answer,
@@ -757,7 +759,12 @@ static int setformat(struct chan_oss_pvt *o, int mode)
 /*
  * some of the standard methods supported by channels.
  */
-static int oss_digit(struct ast_channel *c, char digit)
+static int oss_digit_begin(struct ast_channel *c, char digit)
+{
+	return 0;
+}
+
+static int oss_digit_end(struct ast_channel *c, char digit)
 {
 	/* no better use for received digits than print them */
 	ast_verbose( " << Console Received digit %c >> \n", digit);
diff --git a/channels/chan_phone.c b/channels/chan_phone.c
index 87e971abbf..bb31ebae6a 100644
--- a/channels/chan_phone.c
+++ b/channels/chan_phone.c
@@ -161,7 +161,8 @@ static char cid_num[AST_MAX_EXTENSION];
 static char cid_name[AST_MAX_EXTENSION];
 
 static struct ast_channel *phone_request(const char *type, int format, void *data, int *cause);
-static int phone_digit(struct ast_channel *ast, char digit);
+static int phone_digit_begin(struct ast_channel *ast, char digit);
+static int phone_digit_end(struct ast_channel *ast, char digit);
 static int phone_call(struct ast_channel *ast, char *dest, int timeout);
 static int phone_hangup(struct ast_channel *ast);
 static int phone_answer(struct ast_channel *ast);
@@ -177,7 +178,8 @@ static const struct ast_channel_tech phone_tech = {
 	.description = tdesc,
 	.capabilities = AST_FORMAT_G723_1 | AST_FORMAT_SLINEAR | AST_FORMAT_ULAW,
 	.requester = phone_request,
-	.send_digit = phone_digit,
+	.send_digit_begin = phone_digit_begin,
+	.send_digit_end = phone_digit_end,
 	.call = phone_call,
 	.hangup = phone_hangup,
 	.answer = phone_answer,
@@ -192,7 +194,8 @@ static struct ast_channel_tech phone_tech_fxs = {
 	.type = "Phone",
 	.description = tdesc,
 	.requester = phone_request,
-	.send_digit = phone_digit,
+	.send_digit_begin = phone_digit_begin,
+	.send_digit_end = phone_digit_end,
 	.call = phone_call,
 	.hangup = phone_hangup,
 	.answer = phone_answer,
@@ -240,7 +243,13 @@ static int phone_fixup(struct ast_channel *old, struct ast_channel *new)
 	return 0;
 }
 
-static int phone_digit(struct ast_channel *ast, char digit)
+static int phone_digit_begin(struct ast_channel *chan, char digit)
+{
+	/* XXX Modify this callback to let Asterisk support controlling the length of DTMF */
+	return 0;
+}
+
+static int phone_digit_end(struct ast_channel *ast, char digit)
 {
 	struct phone_pvt *p;
 	int outdigit;
@@ -329,7 +338,7 @@ static int phone_call(struct ast_channel *ast, char *dest, int timeout)
 		{
 		  digit++;
 		  while (*digit)
-		    phone_digit(ast, *digit++);
+		    phone_digit_end(ast, *digit++);
 		}
 	}
  
diff --git a/channels/chan_sip.c b/channels/chan_sip.c
index 948dc3bc63..aed8b5828f 100644
--- a/channels/chan_sip.c
+++ b/channels/chan_sip.c
@@ -743,9 +743,10 @@ struct sip_auth {
 #define SIP_PAGE2_CALL_ONHOLD		(3 << 23)	/*!< Call states */
 #define SIP_PAGE2_CALL_ONHOLD_ONEDIR	(1 << 23)	/*!< 23: One directional hold */
 #define SIP_PAGE2_CALL_ONHOLD_INACTIVE	(2 << 24)	/*!< 24: Inactive  */
+#define SIP_PAGE2_RFC2833_COMPENSATE    (1 << 26)
 
 #define SIP_PAGE2_FLAGS_TO_COPY \
-	(SIP_PAGE2_ALLOWSUBSCRIBE | SIP_PAGE2_ALLOWOVERLAP | SIP_PAGE2_VIDEOSUPPORT | SIP_PAGE2_T38SUPPORT)
+	(SIP_PAGE2_ALLOWSUBSCRIBE | SIP_PAGE2_ALLOWOVERLAP | SIP_PAGE2_VIDEOSUPPORT | SIP_PAGE2_T38SUPPORT | SIP_PAGE2_RFC2833_COMPENSATE)
 
 /* SIP packet flags */
 #define SIP_PKT_DEBUG		(1 << 0)	/*!< Debug this packet */
@@ -1161,7 +1162,8 @@ static int sip_write(struct ast_channel *ast, struct ast_frame *frame);
 static int sip_indicate(struct ast_channel *ast, int condition, const void *data, size_t datalen);
 static int sip_transfer(struct ast_channel *ast, const char *dest);
 static int sip_fixup(struct ast_channel *oldchan, struct ast_channel *newchan);
-static int sip_senddigit(struct ast_channel *ast, char digit);
+static int sip_senddigit_begin(struct ast_channel *ast, char digit);
+static int sip_senddigit_end(struct ast_channel *ast, char digit);
 
 /*--- Transmitting responses and requests */
 static int sipsock_read(int *id, int fd, short events, void *ignore);
@@ -1512,7 +1514,8 @@ static const struct ast_channel_tech sip_tech = {
 	.indicate = sip_indicate,
 	.transfer = sip_transfer,
 	.fixup = sip_fixup,
-	.send_digit = sip_senddigit,
+	.send_digit_begin = sip_senddigit_begin,
+	.send_digit_end = sip_senddigit_end,
 	.bridge = ast_rtp_bridge,
 	.send_text = sip_sendtext,
 };
@@ -2515,12 +2518,14 @@ static int create_addr_from_peer(struct sip_pvt *r, struct sip_peer *peer)
 			ast_log(LOG_DEBUG, "Setting NAT on RTP to %s\n", natflags ? "On" : "Off");
 		ast_rtp_setnat(r->rtp, natflags);
 		ast_rtp_setdtmf(r->rtp, ast_test_flag(&r->flags[0], SIP_DTMF) != SIP_DTMF_INFO);
+		ast_rtp_setdtmfcompensate(r->rtp, ast_test_flag(&r->flags[1], SIP_PAGE2_RFC2833_COMPENSATE));
 	}
 	if (r->vrtp) {
 		if (option_debug)
 			ast_log(LOG_DEBUG, "Setting NAT on VRTP to %s\n", natflags ? "On" : "Off");
 		ast_rtp_setnat(r->vrtp, natflags);
 		ast_rtp_setdtmf(r->vrtp, 0);
+		ast_rtp_setdtmfcompensate(r->vrtp, 0);
 	}
 	if (r->udptl) {
 		if (option_debug)
@@ -3441,9 +3446,31 @@ static int sip_fixup(struct ast_channel *oldchan, struct ast_channel *newchan)
 	return ret;
 }
 
+static int sip_senddigit_begin(struct ast_channel *ast, char digit)
+{
+	struct sip_pvt *p = ast->tech_pvt;
+	int res = 0;
+
+	ast_mutex_lock(&p->lock);
+	switch (ast_test_flag(&p->flags[0], SIP_DTMF)) {
+	case SIP_DTMF_INBAND:
+		res = -1; /* Tell Asterisk to generate inband indications */
+		break;
+	case SIP_DTMF_RFC2833:
+		if (p->rtp)
+			ast_rtp_senddigit_begin(p->rtp, digit);
+		break;
+	default:
+		break;
+	}
+	ast_mutex_unlock(&p->lock);
+
+	return res;
+}
+
 /*! \brief Send DTMF character on SIP channel
 	within one call, we're able to transmit in many methods simultaneously */
-static int sip_senddigit(struct ast_channel *ast, char digit)
+static int sip_senddigit_end(struct ast_channel *ast, char digit)
 {
 	struct sip_pvt *p = ast->tech_pvt;
 	int res = 0;
@@ -3455,13 +3482,14 @@ static int sip_senddigit(struct ast_channel *ast, char digit)
 		break;
 	case SIP_DTMF_RFC2833:
 		if (p->rtp)
-			ast_rtp_senddigit(p->rtp, digit);
+			ast_rtp_senddigit_end(p->rtp, digit);
 		break;
 	case SIP_DTMF_INBAND:
-		res = -1;
+		res = -1; /* Tell Asterisk to stop inband indications */
 		break;
 	}
 	ast_mutex_unlock(&p->lock);
+
 	return res;
 }
 
@@ -4049,10 +4077,12 @@ static struct sip_pvt *sip_alloc(ast_string_field callid, struct sockaddr_in *si
 			return NULL;
 		}
 		ast_rtp_setdtmf(p->rtp, ast_test_flag(&p->flags[0], SIP_DTMF) != SIP_DTMF_INFO);
+		ast_rtp_setdtmfcompensate(p->rtp, ast_test_flag(&p->flags[1], SIP_PAGE2_RFC2833_COMPENSATE));
 		ast_rtp_settos(p->rtp, global_tos_audio);
 		if (p->vrtp) {
 			ast_rtp_settos(p->vrtp, global_tos_video);
 			ast_rtp_setdtmf(p->vrtp, 0);
+			ast_rtp_setdtmfcompensate(p->vrtp, 0);
 		}
 		if (p->udptl)
 			ast_udptl_settos(p->udptl, global_tos_audio);
@@ -12835,6 +12865,7 @@ static int handle_request_invite(struct sip_pvt *p, struct sip_request *req, int
 		extract_uri(p, req);			/* Get the Contact URI */
 		build_contact(p);			/* Build our contact header */
 		ast_rtp_setdtmf(p->rtp, ast_test_flag(&p->flags[0], SIP_DTMF) != SIP_DTMF_INFO);
+		ast_rtp_setdtmfcompensate(p->rtp, ast_test_flag(&p->flags[1], SIP_PAGE2_RFC2833_COMPENSATE));
 
 		if (!replace_id && gotdest) {	/* No matching extension found */
 			if (gotdest == 1 && ast_test_flag(&p->flags[1], SIP_PAGE2_ALLOWOVERLAP)) {
@@ -14856,6 +14887,9 @@ static int handle_common_options(struct ast_flags *flags, struct ast_flags *mask
 	} else if (!strcasecmp(v->name, "t38pt_tcp")) {
 		ast_set_flag(&mask[1], SIP_PAGE2_T38SUPPORT_TCP);
 		ast_set2_flag(&flags[1], ast_true(v->value), SIP_PAGE2_T38SUPPORT_TCP);
+	} else if (!strcasecmp(v->name, "rfc2833compensate")) {
+		ast_set_flag(&mask[1], SIP_PAGE2_RFC2833_COMPENSATE);
+		ast_set2_flag(&flags[1], ast_true(v->value), SIP_PAGE2_RFC2833_COMPENSATE);
 	}
 
 	return res;
diff --git a/channels/chan_skinny.c b/channels/chan_skinny.c
index b05fabb9b1..97c12455e8 100644
--- a/channels/chan_skinny.c
+++ b/channels/chan_skinny.c
@@ -1013,7 +1013,8 @@ static struct ast_frame *skinny_read(struct ast_channel *ast);
 static int skinny_write(struct ast_channel *ast, struct ast_frame *frame);
 static int skinny_indicate(struct ast_channel *ast, int ind, const void *data, size_t datalen);
 static int skinny_fixup(struct ast_channel *oldchan, struct ast_channel *newchan);
-static int skinny_senddigit(struct ast_channel *ast, char digit);
+static int skinny_senddigit_begin(struct ast_channel *ast, char digit);
+static int skinny_senddigit_end(struct ast_channel *ast, char digit);
 
 static const struct ast_channel_tech skinny_tech = {
 	.type = "Skinny",
@@ -1028,7 +1029,8 @@ static const struct ast_channel_tech skinny_tech = {
 	.write = skinny_write,
 	.indicate = skinny_indicate,
 	.fixup = skinny_fixup,
-	.send_digit = skinny_senddigit,
+	.send_digit_begin = skinny_senddigit_begin,
+	.send_digit_end = skinny_senddigit_end,
 /*	.bridge = ast_rtp_bridge, */
 };
 
@@ -2467,7 +2469,12 @@ static int skinny_fixup(struct ast_channel *oldchan, struct ast_channel *newchan
 	return 0;
 }
 
-static int skinny_senddigit(struct ast_channel *ast, char digit)
+static int skinny_senddigit_begin(struct ast_channel *ast, char digit)
+{
+	return -1; /* Start inband indications */
+}
+
+static int skinny_senddigit_end(struct ast_channel *ast, char digit)
 {
 #if 0
 	struct skinny_subchannel *sub = ast->tech_pvt;
@@ -2478,7 +2485,7 @@ static int skinny_senddigit(struct ast_channel *ast, char digit)
 	sprintf(tmp, "%d", digit);
 	transmit_tone(d->session, digit);
 #endif
-	return -1;
+	return -1; /* Stop inband indications */
 }
 
 static char *control2str(int ind) {
diff --git a/channels/chan_vpb.cc b/channels/chan_vpb.cc
index 0a98590d95..26e1beea0d 100644
--- a/channels/chan_vpb.cc
+++ b/channels/chan_vpb.cc
@@ -341,9 +341,9 @@ static struct vpb_pvt {
 
 static struct ast_channel *vpb_new(struct vpb_pvt *i, enum ast_channel_state state, char *context);
 static void *do_chanreads(void *pvt);
-
 static struct ast_channel *vpb_request(const char *type, int format, void *data, int *cause);
-static int vpb_digit(struct ast_channel *ast, char digit);
+static int vpb_digit_begin(struct ast_channel *ast, char digit);
+static int vpb_digit_end(struct ast_channel *ast, char digit);
 static int vpb_call(struct ast_channel *ast, char *dest, int timeout);
 static int vpb_hangup(struct ast_channel *ast);
 static int vpb_answer(struct ast_channel *ast);
@@ -360,9 +360,8 @@ static struct ast_channel_tech vpb_tech = {
 	properties: 0,
 	requester: vpb_request,
 	devicestate: NULL,
-	send_digit: vpb_digit,
-	send_digit_begin: NULL,
-	send_digit_end: NULL,
+	send_digit_begin: vpb_digit_begin,
+	send_digit_end: vpb_digit_end,
 	call: vpb_call,
 	hangup: vpb_hangup,
 	answer: vpb_answer,
@@ -389,9 +388,8 @@ static struct ast_channel_tech vpb_tech_indicate = {
 	properties: 0,
 	requester: vpb_request,
 	devicestate: NULL,
-	send_digit: vpb_digit,
-	send_digit_begin: NULL,
-	send_digit_end: NULL,
+	send_digit_begin: vpb_digit_begin,
+	send_digit_end: vpb_digit_end,
 	call: vpb_call,
 	hangup: vpb_hangup,
 	answer: vpb_answer,
@@ -863,11 +861,11 @@ static inline int monitor_handle_owned(struct vpb_pvt *p, VPB_EVENT *e)
 				vpb_timer_stop(p->ring_timer);
 				vpb_timer_start(p->ring_timer);
 			} else
-				f.frametype = -1; /* ignore ring on station port. */
+				f.frametype = AST_FRAME_NULL; /* ignore ring on station port. */
 			break;
 
 		case VPB_RING_OFF:
-			f.frametype = -1;
+			f.frametype = AST_FRAME_NULL;
 			break;
 
 		case VPB_TIMEREXP:
@@ -876,12 +874,12 @@ static inline int monitor_handle_owned(struct vpb_pvt *p, VPB_EVENT *e)
 				p->state = VPB_STATE_PLAYBUSY;
 				vpb_timer_stop(p->busy_timer);
 				vpb_timer_start(p->busy_timer);
-				f.frametype = -1;
+				f.frametype = AST_FRAME_NULL;
 			} else if (e->data == p->ringback_timer_id) {
 				playtone(p->handle, &Ringbacktone);
 				vpb_timer_stop(p->ringback_timer);
 				vpb_timer_start(p->ringback_timer);
-				f.frametype = -1;
+				f.frametype = AST_FRAME_NULL;
 			} else if (e->data == p->ring_timer_id) {
 				/* We didnt get another ring in time! */
 				if (p->owner->_state != AST_STATE_UP)  {
@@ -890,23 +888,23 @@ static inline int monitor_handle_owned(struct vpb_pvt *p, VPB_EVENT *e)
 					f.subclass = AST_CONTROL_HANGUP;
 				} else {
 					vpb_timer_stop(p->ring_timer);
-					f.frametype = -1;
+					f.frametype = AST_FRAME_NULL;
 				}
 				
 			} else {
-				f.frametype = -1; /* Ignore. */
+				f.frametype = AST_FRAME_NULL; /* Ignore. */
 			}
 			break;
 
 		case VPB_DTMF_DOWN:
 		case VPB_DTMF:
 			if (use_ast_dtmfdet){
-				f.frametype = -1;
+				f.frametype = AST_FRAME_NULL;
 			} else if (p->owner->_state == AST_STATE_UP) {
 					f.frametype = AST_FRAME_DTMF;
 					f.subclass = e->data;
 			} else
-				f.frametype = -1;
+				f.frametype = AST_FRAME_NULL;
 			break;
 
 		case VPB_TONEDETECT:
@@ -950,11 +948,11 @@ static inline int monitor_handle_owned(struct vpb_pvt *p, VPB_EVENT *e)
 					f.subclass = AST_CONTROL_HANGUP;
 				} else {
 					p->lastgrunt = ast_tvnow();
-					f.frametype = -1;
+					f.frametype = AST_FRAME_NULL;
 				}
 			} 
 			else {
-				f.frametype = -1;
+				f.frametype = AST_FRAME_NULL;
 			}
 			break;
 
@@ -970,7 +968,7 @@ static inline int monitor_handle_owned(struct vpb_pvt *p, VPB_EVENT *e)
 				f.subclass = AST_CONTROL_HANGUP;
 			#else
 			ast_log(LOG_NOTICE,"%s: Got call progress callback but blind dialing \n", p->dev); 
-			f.frametype = -1;
+			f.frametype = AST_FRAME_NULL;
 			#endif
 			break;
 
@@ -983,14 +981,14 @@ static inline int monitor_handle_owned(struct vpb_pvt *p, VPB_EVENT *e)
 				if (p->owner->_state == AST_STATE_UP) 
 					f.subclass = AST_CONTROL_HANGUP;
 				else
-					f.frametype = -1;
+					f.frametype = AST_FRAME_NULL;
 			}
 			break;
 		case VPB_LOOP_ONHOOK:
 			if (p->owner->_state == AST_STATE_UP)
 				f.subclass = AST_CONTROL_HANGUP;
 			else
-				f.frametype = -1;
+				f.frametype = AST_FRAME_NULL;
 			break;
 		case VPB_STATION_ONHOOK:
 			f.subclass = AST_CONTROL_HANGUP;
@@ -1009,22 +1007,22 @@ static inline int monitor_handle_owned(struct vpb_pvt *p, VPB_EVENT *e)
 				if (option_verbose > 1) 
 					ast_verbose(VERBOSE_PREFIX_2 "%s: Dialend\n", p->dev);
 			} else {
-				f.frametype = -1;
+				f.frametype = AST_FRAME_NULL;
 			}
 			break;
 
 		case VPB_PLAY_UNDERFLOW:
-			f.frametype = -1;
+			f.frametype = AST_FRAME_NULL;
 			vpb_reset_play_fifo_alarm(p->handle);
 			break;
 
 		case VPB_RECORD_OVERFLOW:
-			f.frametype = -1;
+			f.frametype = AST_FRAME_NULL;
 			vpb_reset_record_fifo_alarm(p->handle);
 			break;
 
 		default:
-			f.frametype = -1;
+			f.frametype = AST_FRAME_NULL;
 			break;
 	}
 
@@ -1831,7 +1829,12 @@ static int vpb_fixup(struct ast_channel *oldchan, struct ast_channel *newchan)
 	return 0;
 }
 
-static int vpb_digit(struct ast_channel *ast, char digit)
+static int vpb_digit_begin(struct ast_channel *ast, char digit)
+{
+	/* XXX Modify this callback to let Asterisk control the length of DTMF */
+	return 0;
+}
+static int vpb_digit_end(struct ast_channel *ast, char digit)
 {
 	struct vpb_pvt *p = (struct vpb_pvt *)ast->tech_pvt;
 	char s[2];
diff --git a/channels/chan_zap.c b/channels/chan_zap.c
index ae3e94277a..7c860ff502 100644
--- a/channels/chan_zap.c
+++ b/channels/chan_zap.c
@@ -690,10 +690,12 @@ static struct zt_pvt {
 #endif	
 	int polarity;
 	int dsp_features;
+	char begindigit;
 } *iflist = NULL, *ifend = NULL;
 
 static struct ast_channel *zt_request(const char *type, int format, void *data, int *cause);
-static int zt_digit(struct ast_channel *ast, char digit);
+static int zt_digit_begin(struct ast_channel *ast, char digit);
+static int zt_digit_end(struct ast_channel *ast, char digit);
 static int zt_sendtext(struct ast_channel *c, const char *text);
 static int zt_call(struct ast_channel *ast, char *rdest, int timeout);
 static int zt_hangup(struct ast_channel *ast);
@@ -711,7 +713,8 @@ static const struct ast_channel_tech zap_tech = {
 	.description = tdesc,
 	.capabilities = AST_FORMAT_SLINEAR | AST_FORMAT_ULAW | AST_FORMAT_ALAW,
 	.requester = zt_request,
-	.send_digit = zt_digit,
+	.send_digit_begin = zt_digit_begin,
+	.send_digit_end = zt_digit_end,
 	.send_text = zt_sendtext,
 	.call = zt_call,
 	.hangup = zt_hangup,
@@ -1002,45 +1005,113 @@ static int unalloc_sub(struct zt_pvt *p, int x)
 	return 0;
 }
 
-static int zt_digit(struct ast_channel *ast, char digit)
+static int digit_to_dtmfindex(char digit)
 {
-	ZT_DIAL_OPERATION zo;
-	struct zt_pvt *p;
+	if (isdigit(digit))
+		return ZT_TONE_DTMF_BASE + (digit - '0');
+	else if (digit >= 'A' && digit <= 'D')
+		return ZT_TONE_DTMF_A + (digit - 'A');
+	else if (digit >= 'a' && digit <= 'd')
+		return ZT_TONE_DTMF_A + (digit - 'a');
+	else if (digit == '*')
+		return ZT_TONE_DTMF_s;
+	else if (digit == '#')
+		return ZT_TONE_DTMF_p;
+	else
+		return -1;
+}
+
+static int zt_digit_begin(struct ast_channel *chan, char digit)
+{
+	struct zt_pvt *pvt;
+	int index;
+	int dtmf = -1;
+	
+	pvt = chan->tech_pvt;
+
+	ast_mutex_lock(&pvt->lock);
+
+	index = zt_get_index(chan, pvt, 0);
+
+	if ((index != SUB_REAL) || !pvt->owner)
+		goto out;
+
+#ifdef HAVE_PRI
+	if ((pvt->sig == SIG_PRI) && (chan->_state == AST_STATE_DIALING) && !pvt->proceeding) {
+		if (pvt->setup_ack) {
+			if (!pri_grab(pvt, pvt->pri)) {
+				pri_information(pvt->pri->pri, pvt->call, digit);
+				pri_rel(pvt->pri);
+			} else
+				ast_log(LOG_WARNING, "Unable to grab PRI on span %d\n", pvt->span);
+		} else if (strlen(pvt->dialdest) < sizeof(pvt->dialdest) - 1) {
+			int res;
+			ast_log(LOG_DEBUG, "Queueing digit '%c' since setup_ack not yet received\n", digit);
+			res = strlen(pvt->dialdest);
+			pvt->dialdest[res++] = digit;
+			pvt->dialdest[res] = '\0';
+		}
+		goto out;
+	}
+#endif
+	if ((dtmf = digit_to_dtmfindex(digit)) == -1)
+		goto out;
+
+	if (pvt->pulse || ioctl(pvt->subs[SUB_REAL].zfd, ZT_SENDTONE, &dtmf)) {
+		int res;
+		ZT_DIAL_OPERATION zo = {
+			.op = ZT_DIAL_OP_APPEND,
+			.dialstr[0] = 'T',
+			.dialstr[1] = digit,
+			.dialstr[2] = 0,
+		};
+		if ((res = ioctl(pvt->subs[SUB_REAL].zfd, ZT_DIAL, &zo)))
+			ast_log(LOG_WARNING, "Couldn't dial digit %c\n", digit);
+		else
+			pvt->dialing = 1;
+	} else {
+		ast_log(LOG_DEBUG, "Started VLDTMF digit '%c'\n", digit);
+		pvt->dialing = 1;
+		pvt->begindigit = digit;
+	}
+
+out:
+	ast_mutex_unlock(&pvt->lock);
+
+	return 0; /* Tell Asterisk not to generate inband indications */
+}
+
+static int zt_digit_end(struct ast_channel *chan, char digit)
+{
+	struct zt_pvt *pvt;
 	int res = 0;
 	int index;
-	p = ast->tech_pvt;
-	ast_mutex_lock(&p->lock);
-	index = zt_get_index(ast, p, 0);
-	if ((index == SUB_REAL) && p->owner) {
+	
+	pvt = chan->tech_pvt;
+
+	ast_mutex_lock(&pvt->lock);
+	
+	index = zt_get_index(chan, pvt, 0);
+
+	if ((index != SUB_REAL) || !pvt->owner || pvt->pulse)
+		goto out;
+
 #ifdef HAVE_PRI
-		if ((p->sig == SIG_PRI) && (ast->_state == AST_STATE_DIALING) && !p->proceeding) {
-			if (p->setup_ack) {
-				if (!pri_grab(p, p->pri)) {
-					pri_information(p->pri->pri,p->call,digit);
-					pri_rel(p->pri);
-				} else
-					ast_log(LOG_WARNING, "Unable to grab PRI on span %d\n", p->span);
-			} else if (strlen(p->dialdest) < sizeof(p->dialdest) - 1) {
-				ast_log(LOG_DEBUG, "Queueing digit '%c' since setup_ack not yet received\n", digit);
-				res = strlen(p->dialdest);
-				p->dialdest[res++] = digit;
-				p->dialdest[res] = '\0';
-			}
-		} else {
-#else
-		{
+	/* This means that the digit was already sent via PRI signalling */
+	if (pvt->sig == SIG_PRI && !pvt->begindigit)
+		goto out;
 #endif
-			zo.op = ZT_DIAL_OP_APPEND;
-			zo.dialstr[0] = 'T';
-			zo.dialstr[1] = digit;
-			zo.dialstr[2] = 0;
-			if ((res = ioctl(p->subs[SUB_REAL].zfd, ZT_DIAL, &zo)))
-				ast_log(LOG_WARNING, "Couldn't dial digit %c\n", digit);
-			else
-				p->dialing = 1;
-		}
+
+	if (pvt->begindigit) {
+		ast_log(LOG_DEBUG, "Ending VLDTMF digit '%c'\n", digit);
+		res = ioctl(pvt->subs[SUB_REAL].zfd, ZT_SENDTONE, -1);
+		pvt->dialing = 0;
+		pvt->begindigit = 0;
 	}
-	ast_mutex_unlock(&p->lock);
+
+out:
+	ast_mutex_unlock(&pvt->lock);
+
 	return res;
 }
 
@@ -3543,21 +3614,14 @@ static struct ast_frame *zt_handle_event(struct ast_channel *ast)
 	ast_log(LOG_DEBUG, "Got event %s(%d) on channel %d (index %d)\n", event2str(res), res, p->channel, index);
 
 	if (res & (ZT_EVENT_PULSEDIGIT | ZT_EVENT_DTMFUP)) {
-		if (res & ZT_EVENT_PULSEDIGIT)
-			p->pulsedial = 1;
-		else
-			p->pulsedial = 0;
+		p->pulsedial =  (res & ZT_EVENT_PULSEDIGIT) ? 1 : 0;
 		ast_log(LOG_DEBUG, "Detected %sdigit '%c'\n", p->pulsedial ? "pulse ": "", res & 0xff);
 #ifdef HAVE_PRI
 		if (!p->proceeding && p->sig == SIG_PRI && p->pri && p->pri->overlapdial) {
 			/* absorb event */
 		} else {
 #endif
-			/* Send a DTMF event for 'legacy' channels and all applications,
-			   and a DTMF_BEGIN event for channels that handle variable duration
-			   DTMF events
-			*/
-			p->subs[index].f.frametype = AST_FRAME_DTMF_BEGIN;
+			p->subs[index].f.frametype = AST_FRAME_DTMF_END;
 			p->subs[index].f.subclass = res & 0xff;
 #ifdef HAVE_PRI
 		}
@@ -3571,9 +3635,6 @@ static struct ast_frame *zt_handle_event(struct ast_channel *ast)
 		ast_log(LOG_DEBUG, "DTMF Down '%c'\n", res & 0xff);
 		/* Mute conference */
 		zt_confmute(p, 1);
-		/* Send a DTMF_BEGIN event for devices that want variable
-		   duration DTMF events
-		*/
 		p->subs[index].f.frametype = AST_FRAME_DTMF_BEGIN;
 		p->subs[index].f.subclass = res & 0xff;
 		return &p->subs[index].f;
diff --git a/channels/iax2-parser.c b/channels/iax2-parser.c
index 94aaf4c706..56c45e2422 100644
--- a/channels/iax2-parser.c
+++ b/channels/iax2-parser.c
@@ -395,7 +395,7 @@ void iax_showframe(struct iax_frame *f, struct ast_iax2_full_hdr *fhi, int rx, s
 {
 	const char *frames[] = {
 		"(0?)",
-		"DTMF   ",
+		"DTMF_E ",
 		"VOICE  ",
 		"VIDEO  ",
 		"CONTROL",
@@ -404,7 +404,10 @@ void iax_showframe(struct iax_frame *f, struct ast_iax2_full_hdr *fhi, int rx, s
 		"TEXT   ",
 		"IMAGE  ",
 		"HTML   ",
-		"CNG    " };
+		"CNG    ",
+		"MODEM  ",
+		"DTMF_B ",
+	};
 	const char *iaxs[] = {
 		"(0?)",
 		"NEW    ",
@@ -508,7 +511,7 @@ void iax_showframe(struct iax_frame *f, struct ast_iax2_full_hdr *fhi, int rx, s
 	} else {
 		class = frames[(int)fh->type];
 	}
-	if (fh->type == AST_FRAME_DTMF) {
+	if (fh->type == AST_FRAME_DTMF_BEGIN || fh->type == AST_FRAME_DTMF_END) {
 		sprintf(subclass2, "%c", fh->csub);
 		subclass = subclass2;
 	} else if (fh->type == AST_FRAME_IAX) {
diff --git a/configs/sip.conf.sample b/configs/sip.conf.sample
index 3f24034cc1..b04c60e77e 100644
--- a/configs/sip.conf.sample
+++ b/configs/sip.conf.sample
@@ -417,7 +417,7 @@ srvlookup=yes			; Enable DNS SRV lookups on outbound calls
 ; subscribecontext	      subscribecontext
 ; videosupport		      videosupport
 ; maxcallbitrate	      maxcallbitrate
-;                             mailbox
+; rfc2833compensate           mailbox
 ;                             username
 ;                             template
 ;                             fromdomain
@@ -431,6 +431,7 @@ srvlookup=yes			; Enable DNS SRV lookups on outbound calls
 ;                             rtpholdtimeout
 ;                             sendrpid
 ;                             outboundproxy
+;                             rfc2833compensate
 
 ;[sip_proxy]
 ; For incoming calls only. Example: FWD (Free World Dialup)
@@ -587,3 +588,9 @@ srvlookup=yes			; Enable DNS SRV lookups on outbound calls
 				; Normally you do NOT need to set this parameter
 ;setvar=CUSTID=5678		; Channel variable to be set for all calls from this device
 
+;[pre14-asterisk]
+;type=friend
+;secret=digium
+;host=dynamic
+;rfc2833compensate=yes		; Compensate for pre-1.4 DTMF transmission from another Asterisk machine.
+				; You must have this turned on or DTMF reception will work improperly.
diff --git a/include/asterisk/channel.h b/include/asterisk/channel.h
index 95cb3196eb..d54a1bdd79 100644
--- a/include/asterisk/channel.h
+++ b/include/asterisk/channel.h
@@ -200,13 +200,11 @@ struct ast_channel_tech {
 
 	int (* const devicestate)(void *data);	/*!< Devicestate call back */
 
-	int (* const send_digit)(struct ast_channel *chan, char digit);	/*!< Send a literal DTMF digit */
-
 	/*! \brief Start sending a literal DTMF digit */
 	int (* const send_digit_begin)(struct ast_channel *chan, char digit);
 
-	/*! \brief Stop sending the last literal DTMF digit */
-	int (* const send_digit_end)(struct ast_channel *chan);
+	/*! \brief Stop sending a literal DTMF digit */
+	int (* const send_digit_end)(struct ast_channel *chan, char digit);
 
 	/*! \brief Call a given phone number (address, etc), but don't
 	   take longer than timeout seconds to do so.  */
@@ -424,8 +422,12 @@ struct ast_channel {
 	struct ast_channel_spy_list *spies;		/*!< Chan Spy stuff */
 	struct ast_channel_whisper_buffer *whisper;	/*!< Whisper Paging buffer */
 	AST_LIST_ENTRY(ast_channel) chan_list;		/*!< For easy linking */
+	
 	struct ast_jb jb;				/*!< The jitterbuffer state  */
 
+	char emulate_dtmf_digit;			/*!< Digit being emulated */
+	unsigned int emulate_dtmf_samples;		/*!< Number of samples left to emulate DTMF for */
+
 	/*! \brief Data stores on the channel */
 	AST_LIST_HEAD_NOLOCK(datastores, ast_datastore) datastores;
 };
@@ -443,29 +445,34 @@ enum {
 /*! \brief ast_channel flags */
 enum {
 	/*! Queue incoming dtmf, to be released when this flag is turned off */
-	AST_FLAG_DEFER_DTMF =  (1 << 1),
+	AST_FLAG_DEFER_DTMF =   (1 << 1),
 	/*! write should be interrupt generator */
-	AST_FLAG_WRITE_INT =   (1 << 2),
+	AST_FLAG_WRITE_INT =    (1 << 2),
 	/*! a thread is blocking on this channel */
-	AST_FLAG_BLOCKING =    (1 << 3),
+	AST_FLAG_BLOCKING =     (1 << 3),
 	/*! This is a zombie channel */
-	AST_FLAG_ZOMBIE =      (1 << 4),
+	AST_FLAG_ZOMBIE =       (1 << 4),
 	/*! There is an exception pending */
-	AST_FLAG_EXCEPTION =   (1 << 5),
+	AST_FLAG_EXCEPTION =    (1 << 5),
 	/*! Listening to moh XXX anthm promises me this will disappear XXX */
-	AST_FLAG_MOH =         (1 << 6),
+	AST_FLAG_MOH =          (1 << 6),
 	/*! This channel is spying on another channel */
-	AST_FLAG_SPYING =      (1 << 7),
+	AST_FLAG_SPYING =       (1 << 7),
 	/*! This channel is in a native bridge */
-	AST_FLAG_NBRIDGE =     (1 << 8),
+	AST_FLAG_NBRIDGE =      (1 << 8),
 	/*! the channel is in an auto-incrementing dialplan processor,
 	 *  so when ->priority is set, it will get incremented before
 	 *  finding the next priority to run */
-	AST_FLAG_IN_AUTOLOOP = (1 << 9),
+	AST_FLAG_IN_AUTOLOOP =  (1 << 9),
 	/*! This is an outgoing call */
-	AST_FLAG_OUTGOING =    (1 << 10),
+	AST_FLAG_OUTGOING =     (1 << 10),
 	/*! This channel is being whispered on */
-	AST_FLAG_WHISPER =     (1 << 11),
+	AST_FLAG_WHISPER =      (1 << 11),
+	/*! A DTMF_BEGIN frame has been read from this channel, but not yet an END */
+	AST_FLAG_IN_DTMF =      (1 << 12),
+	/*! A DTMF_END was received when not IN_DTMF, so the length of the digit is 
+	 *  currently being emulated */
+	AST_FLAG_EMULATE_DTMF = (1 << 13),
 };
 
 /*! \brief ast_bridge_config flags */
@@ -876,6 +883,9 @@ int ast_recvchar(struct ast_channel *chan, int timeout);
  */
 int ast_senddigit(struct ast_channel *chan, char digit);
 
+int ast_senddigit_begin(struct ast_channel *chan, char digit);
+int ast_senddigit_end(struct ast_channel *chan, char digit);
+
 /*! \brief Receives a text string from a channel
  * Read a string of text from a channel
  * \param chan channel to act upon
diff --git a/include/asterisk/frame.h b/include/asterisk/frame.h
index 0c9c651d27..bc7273a092 100644
--- a/include/asterisk/frame.h
+++ b/include/asterisk/frame.h
@@ -85,11 +85,46 @@ struct ast_codec_pref {
 
 */
 
+/*!
+ * \brief Frame types 
+ *
+ * \note It is important that the values of each frame type are never changed,
+ *       because it will break backwards compatability with older versions.
+ */
+enum ast_frame_type {
+	/*! DTMF end event, subclass is the digit */
+	AST_FRAME_DTMF_END = 1,
+	/*! Voice data, subclass is AST_FORMAT_* */
+	AST_FRAME_VOICE,
+	/*! Video frame, maybe?? :) */
+	AST_FRAME_VIDEO,
+	/*! A control frame, subclass is AST_CONTROL_* */
+	AST_FRAME_CONTROL,
+	/*! An empty, useless frame */
+	AST_FRAME_NULL,
+	/*! Inter Asterisk Exchange private frame type */
+	AST_FRAME_IAX,
+	/*! Text messages */
+	AST_FRAME_TEXT,
+	/*! Image Frames */
+	AST_FRAME_IMAGE,
+	/*! HTML Frame */
+	AST_FRAME_HTML,
+	/*! Comfort Noise frame (subclass is level of CNG in -dBov), 
+	    body may include zero or more 8-bit quantization coefficients */
+	AST_FRAME_CNG,
+	/*! Modem-over-IP data streams */
+	AST_FRAME_MODEM,	
+	/*! DTMF begin event, subclass is the digit */
+	AST_FRAME_DTMF_BEGIN,
+};
+#define AST_FRAME_DTMF AST_FRAME_DTMF_END
+
 /*! \brief Data structure associated with a single frame of data
  */
 struct ast_frame {
 	/*! Kind of frame */
-	int frametype;				
+	enum ast_frame_type frametype;				
 	/*! Subclass, frame dependent */
 	int subclass;				
 	/*! Length of data */
@@ -151,35 +186,6 @@ extern struct ast_frame ast_null_frame;
 /*! Need the source be free'd? (haha!) */
 #define AST_MALLOCD_SRC		(1 << 2)
 
-/* Frame types */
-/*! A DTMF digit, subclass is the digit */
-#define AST_FRAME_DTMF		1
-/*! Voice data, subclass is AST_FORMAT_* */
-#define AST_FRAME_VOICE		2
-/*! Video frame, maybe?? :) */
-#define AST_FRAME_VIDEO		3
-/*! A control frame, subclass is AST_CONTROL_* */
-#define AST_FRAME_CONTROL	4
-/*! An empty, useless frame */
-#define AST_FRAME_NULL		5
-/*! Inter Asterisk Exchange private frame type */
-#define AST_FRAME_IAX		6
-/*! Text messages */
-#define AST_FRAME_TEXT		7
-/*! Image Frames */
-#define AST_FRAME_IMAGE		8
-/*! HTML Frame */
-#define AST_FRAME_HTML		9
-/*! Comfort Noise frame (subclass is level of CNG in -dBov), 
-    body may include zero or more 8-bit quantization coefficients */
-#define AST_FRAME_CNG		10
-/*! Modem-over-IP data streams */
-#define AST_FRAME_MODEM		11
-/*! DTMF begin event, subclass is the digit */
-#define AST_FRAME_DTMF_BEGIN	12
-/*! DTMF end event, subclass is the digit */
-#define AST_FRAME_DTMF_END	13
-
 /* MODEM subclasses */
 /*! T.38 Fax-over-IP */
 #define AST_MODEM_T38		1
diff --git a/include/asterisk/rtp.h b/include/asterisk/rtp.h
index f99d4dec63..6780c2adc9 100644
--- a/include/asterisk/rtp.h
+++ b/include/asterisk/rtp.h
@@ -142,7 +142,9 @@ int ast_rtp_fd(struct ast_rtp *rtp);
 
 int ast_rtcp_fd(struct ast_rtp *rtp);
 
-int ast_rtp_senddigit(struct ast_rtp *rtp, char digit);
+int ast_rtp_senddigit_begin(struct ast_rtp *rtp, char digit);
+
+int ast_rtp_senddigit_end(struct ast_rtp *rtp, char digit);
 
 int ast_rtp_sendcng(struct ast_rtp *rtp, int level);
 
@@ -181,6 +183,9 @@ void ast_rtp_setnat(struct ast_rtp *rtp, int nat);
 /*! \brief Indicate whether this RTP session is carrying DTMF or not */
 void ast_rtp_setdtmf(struct ast_rtp *rtp, int dtmf);
 
+/*! \brief Compensate for devices that send RFC2833 packets all at once */
+void ast_rtp_setdtmfcompensate(struct ast_rtp *rtp, int compensate);
+
 int ast_rtp_bridge(struct ast_channel *c0, struct ast_channel *c1, int flags, struct ast_frame **fo, struct ast_channel **rc, int timeoutms);
 
 int ast_rtp_proto_register(struct ast_rtp_protocol *proto);
diff --git a/main/app.c b/main/app.c
index 1099feaea3..b6a7f73a9a 100644
--- a/main/app.c
+++ b/main/app.c
@@ -212,10 +212,6 @@ int ast_dtmf_stream(struct ast_channel *chan, struct ast_channel *peer, const ch
 {
 	const char *ptr;
 	int res = 0;
-	struct ast_frame f = {
-		.frametype = AST_FRAME_DTMF,
-		.src = "ast_dtmf_stream"
-	};
 
 	if (!between)
 		between = 100;
@@ -240,11 +236,8 @@ int ast_dtmf_stream(struct ast_channel *chan, struct ast_channel *peer, const ch
 			if (*ptr == 'f' || *ptr == 'F') {
 				/* ignore return values if not supported by channel */
 				ast_indicate(chan, AST_CONTROL_FLASH);
-			} else {
-				f.subclass = *ptr;
-				if ((res = ast_write(chan, &f)))
-					break;
-			}
+			} else
+				ast_senddigit(chan, *ptr);
 			/* pause between digits */
 			if ((res = ast_safe_sleep(chan, between)))
 				break;
diff --git a/main/channel.c b/main/channel.c
index 61df3d2439..6ba79eea6e 100644
--- a/main/channel.c
+++ b/main/channel.c
@@ -102,6 +102,9 @@ unsigned long global_fin = 0, global_fout = 0;
 AST_THREADSTORAGE(state2str_threadbuf, state2str_threadbuf_init);
 #define STATE2STR_BUFSIZE   32
 
+/* XXX 100ms ... this won't work with wideband support */
+#define AST_DEFAULT_EMULATE_DTMF_SAMPLES 800
+
 struct chanlist {
 	const struct ast_channel_tech *tech;
 	AST_LIST_ENTRY(chanlist) list;
@@ -240,7 +243,8 @@ static int show_channeltype(int fd, int argc, char *argv[])
 		"    Indication: %s\n"
 		"     Transfer : %s\n"
 		"  Capabilities: %d\n"
-		"    Send Digit: %s\n"
+		"   Digit Begin: %s\n"
+		"     Digit End: %s\n"
 		"    Send HTML : %s\n"
 		" Image Support: %s\n"
 		"  Text Support: %s\n",
@@ -249,7 +253,8 @@ static int show_channeltype(int fd, int argc, char *argv[])
 		(cl->tech->indicate) ? "yes" : "no",
 		(cl->tech->transfer) ? "yes" : "no",
 		(cl->tech->capabilities) ? cl->tech->capabilities : -1,
-		(cl->tech->send_digit) ? "yes" : "no",
+		(cl->tech->send_digit_begin) ? "yes" : "no",
+		(cl->tech->send_digit_end) ? "yes" : "no",
 		(cl->tech->send_html) ? "yes" : "no",
 		(cl->tech->send_image) ? "yes" : "no",
 		(cl->tech->send_text) ? "yes" : "no"
@@ -1862,8 +1867,10 @@ int ast_waitfordigit_full(struct ast_channel *c, int ms, int audiofd, int cmdfd)
 				/* Write audio if appropriate */
 				if (audiofd > -1)
 					write(audiofd, f->data, f->datalen);
+			default:
+				/* Ignore */
+				break;
 			}
-			/* Ignore */
 			ast_frfree(f);
 		}
 	}
@@ -1881,11 +1888,10 @@ static struct ast_frame *__ast_read(struct ast_channel *chan, int dropaudio)
 	 */
 	ast_channel_lock(chan);
 	if (chan->masq) {
-		if (ast_do_masquerade(chan)) {
+		if (ast_do_masquerade(chan))
 			ast_log(LOG_WARNING, "Failed to perform masquerade\n");
-		} else {
+		else
 			f =  &ast_null_frame;
-		}
 		goto done;
 	}
 
@@ -1897,13 +1903,17 @@ static struct ast_frame *__ast_read(struct ast_channel *chan, int dropaudio)
 	}
 	prestate = chan->_state;
 
-	if (!ast_test_flag(chan, AST_FLAG_DEFER_DTMF) && !ast_strlen_zero(chan->dtmfq)) {
+	if (!ast_test_flag(chan, AST_FLAG_DEFER_DTMF | AST_FLAG_EMULATE_DTMF | AST_FLAG_IN_DTMF) && 
+	    !ast_strlen_zero(chan->dtmfq)) {
 		/* We have DTMF that has been deferred.  Return it now */
-		chan->dtmff.frametype = AST_FRAME_DTMF;
+		chan->dtmff.frametype = AST_FRAME_DTMF_BEGIN;
 		chan->dtmff.subclass = chan->dtmfq[0];
 		/* Drop first digit from the buffer */
 		memmove(chan->dtmfq, chan->dtmfq + 1, sizeof(chan->dtmfq) - 1);
 		f = &chan->dtmff;
+		ast_set_flag(chan, AST_FLAG_EMULATE_DTMF);
+		chan->emulate_dtmf_digit = f->subclass;
+		chan->emulate_dtmf_samples = AST_DEFAULT_EMULATE_DTMF_SAMPLES;
 		goto done;
 	}
 	
@@ -2017,27 +2027,57 @@ static struct ast_frame *__ast_read(struct ast_channel *chan, int dropaudio)
 				}
 			}
 			break;
-		case AST_FRAME_DTMF:
-			ast_log(LOG_DTMF, "DTMF '%c' received on %s\n", f->subclass, chan->name);
-			if (ast_test_flag(chan, AST_FLAG_DEFER_DTMF)) {
+		case AST_FRAME_DTMF_END:
+			ast_log(LOG_DTMF, "DTMF end '%c' received on %s\n", f->subclass, chan->name);
+			if (ast_test_flag(chan, AST_FLAG_DEFER_DTMF | AST_FLAG_EMULATE_DTMF)) {
 				if (strlen(chan->dtmfq) < sizeof(chan->dtmfq) - 2)
 					chan->dtmfq[strlen(chan->dtmfq)] = f->subclass;
 				else
 					ast_log(LOG_WARNING, "Dropping deferred DTMF digits on %s\n", chan->name);
 				ast_frfree(f);
 				f = &ast_null_frame;
-			}
+			} else if (!ast_test_flag(chan, AST_FLAG_IN_DTMF)) {
+				f->frametype = AST_FRAME_DTMF_BEGIN;
+				ast_set_flag(chan, AST_FLAG_EMULATE_DTMF);
+				chan->emulate_dtmf_digit = f->subclass;
+				if (f->samples)
+					chan->emulate_dtmf_samples = f->samples;
+				else
+					chan->emulate_dtmf_samples = AST_DEFAULT_EMULATE_DTMF_SAMPLES;
+			} else 
+				ast_clear_flag(chan, AST_FLAG_IN_DTMF);
 			break;
 		case AST_FRAME_DTMF_BEGIN:
 			ast_log(LOG_DTMF, "DTMF begin '%c' received on %s\n", f->subclass, chan->name);
-			break;
-		case AST_FRAME_DTMF_END:
-			ast_log(LOG_DTMF, "DTMF end '%c' received on %s\n", f->subclass, chan->name);
+			if (ast_test_flag(chan, AST_FLAG_DEFER_DTMF)) {
+				ast_frfree(f);
+				f = &ast_null_frame;
+			} else 
+				ast_set_flag(chan, AST_FLAG_IN_DTMF);
 			break;
 		case AST_FRAME_VOICE:
-			if (dropaudio) {
+			/* The EMULATE_DTMF flag must be cleared here as opposed to when the samples
+			 * first get to zero, because we want to make sure we pass at least one
+			 * voice frame through before starting the next digit, to ensure a gap
+			 * between DTMF digits. */
+			if (ast_test_flag(chan, AST_FLAG_EMULATE_DTMF) && !chan->emulate_dtmf_samples) {
+				ast_clear_flag(chan, AST_FLAG_EMULATE_DTMF);
+				chan->emulate_dtmf_digit = 0;
+			}
+
+			if (dropaudio || ast_test_flag(chan, AST_FLAG_IN_DTMF)) {
 				ast_frfree(f);
 				f = &ast_null_frame;
+			} else if (ast_test_flag(chan, AST_FLAG_EMULATE_DTMF)) {
+				if (f->samples >= chan->emulate_dtmf_samples) {
+					chan->emulate_dtmf_samples = 0;
+					f->frametype = AST_FRAME_DTMF_END;
+					f->subclass = chan->emulate_dtmf_digit;
+				} else {
+					chan->emulate_dtmf_samples -= f->samples;
+					ast_frfree(f);
+					f = &ast_null_frame;
+				}
 			} else if (!(f->subclass & chan->nativeformats)) {
 				/* This frame can't be from the current native formats -- drop it on the
 				   floor */
@@ -2106,6 +2146,9 @@ static struct ast_frame *__ast_read(struct ast_channel *chan, int dropaudio)
 					}
 				}
 			}
+		default:
+			/* Just pass it on! */
+			break;
 		}
 	} else {
 		/* Make sure we always return NULL in the future */
@@ -2258,12 +2301,13 @@ int ast_sendtext(struct ast_channel *chan, const char *text)
 	return res;
 }
 
-static int do_senddigit(struct ast_channel *chan, char digit)
+int ast_senddigit_begin(struct ast_channel *chan, char digit)
 {
 	int res = -1;
 
-	if (chan->tech->send_digit)
-		res = chan->tech->send_digit(chan, digit);
+	if (chan->tech->send_digit_begin)
+		res = chan->tech->send_digit_begin(chan, digit);
+
 	if (res) {
 		/*
 		 * Device does not support DTMF tones, lets fake
@@ -2299,12 +2343,30 @@ static int do_senddigit(struct ast_channel *chan, char digit)
 			ast_log(LOG_DEBUG, "Unable to generate DTMF tone '%c' for '%s'\n", digit, chan->name);
 		}
 	}
+
+	return 0;
+}
+
+int ast_senddigit_end(struct ast_channel *chan, char digit)
+{
+	int res = -1;
+
+	if (chan->tech->send_digit_end)
+		res = chan->tech->send_digit_end(chan, digit);
+
+	if (res && chan->generator)
+		ast_playtones_stop(chan);
+	
 	return 0;
 }
 
 int ast_senddigit(struct ast_channel *chan, char digit)
 {
-	return do_senddigit(chan, digit);
+	ast_senddigit_begin(chan, digit);
+	
+	ast_safe_sleep(chan, 100); /* XXX 100ms ... probably should be configurable */
+	
+	return ast_senddigit_end(chan, digit);
 }
 
 int ast_prod(struct ast_channel *chan)
@@ -2372,17 +2434,16 @@ int ast_write(struct ast_channel *chan, struct ast_frame *fr)
 			chan->tech->indicate(chan, fr->subclass, fr->data, fr->datalen);
 		break;
 	case AST_FRAME_DTMF_BEGIN:
-		res = (chan->tech->send_digit_begin == NULL) ? 0 :
-			chan->tech->send_digit_begin(chan, fr->subclass);
+		ast_clear_flag(chan, AST_FLAG_BLOCKING);
+		ast_channel_unlock(chan);
+		res = ast_senddigit_begin(chan, fr->subclass);
+		ast_channel_lock(chan);
+		CHECK_BLOCKING(chan);
 		break;
 	case AST_FRAME_DTMF_END:
-		res = (chan->tech->send_digit_end == NULL) ? 0 :
-			chan->tech->send_digit_end(chan);
-		break;
-	case AST_FRAME_DTMF:
 		ast_clear_flag(chan, AST_FLAG_BLOCKING);
 		ast_channel_unlock(chan);
-		res = do_senddigit(chan,fr->subclass);
+		res = ast_senddigit_end(chan, fr->subclass);
 		ast_channel_lock(chan);
 		CHECK_BLOCKING(chan);
 		break;
@@ -2467,7 +2528,14 @@ int ast_write(struct ast_channel *chan, struct ast_frame *fr)
 
 			res = chan->tech->write(chan, f);
 		}
-		break;	
+		break;
+	case AST_FRAME_NULL:
+	case AST_FRAME_IAX:
+		/* Ignore these */
+		break;
+	default:
+		res = chan->tech->write(chan, f);
+		break;
 	}
 
 	if (f && f != fr)
@@ -3496,6 +3564,7 @@ static enum ast_bridge_result ast_generic_bridge(struct ast_channel *c0, struct
 				break;
 		}
 		if ((f->frametype == AST_FRAME_VOICE) ||
+		    (f->frametype == AST_FRAME_DTMF_BEGIN) ||
 		    (f->frametype == AST_FRAME_DTMF) ||
 		    (f->frametype == AST_FRAME_VIDEO) ||
 		    (f->frametype == AST_FRAME_IMAGE) ||
@@ -3505,10 +3574,14 @@ static enum ast_bridge_result ast_generic_bridge(struct ast_channel *c0, struct
 			/* monitored dtmf causes exit from bridge */
 			int monitored_source = (who == c0) ? watch_c0_dtmf : watch_c1_dtmf;
 
-			if (f->frametype == AST_FRAME_DTMF && monitored_source) {
+			if (monitored_source && 
+				(f->frametype == AST_FRAME_DTMF_END || 
+				f->frametype == AST_FRAME_DTMF_BEGIN)) {
 				*fo = f;
 				*rc = who;
-				ast_log(LOG_DEBUG, "Got DTMF on channel (%s)\n", who->name);
+				ast_log(LOG_DEBUG, "Got DTMF %s on channel (%s)\n", 
+					f->frametype == AST_FRAME_DTMF_END ? "end" : "begin",
+					who->name);
 				break;
 			}
 			/* Write immediately frames, not passed through jb */
diff --git a/main/dsp.c b/main/dsp.c
index 7e62c8bf72..2992031b4e 100644
--- a/main/dsp.c
+++ b/main/dsp.c
@@ -1497,42 +1497,45 @@ struct ast_frame *ast_dsp_process(struct ast_channel *chan, struct ast_dsp *dsp,
 			} else {
 				if (digit) {
 					/* Thought we saw one last time.  Pretty sure we really have now */
-					if (dsp->thinkdigit) {
-						if ((dsp->thinkdigit != 'x') && (dsp->thinkdigit != digit)) {
-							/* If we found a digit, and we're changing digits, go
-							   ahead and send this one, but DON'T stop confmute because
-							   we're detecting something else, too... */
-							memset(&dsp->f, 0, sizeof(dsp->f));
-							dsp->f.frametype = AST_FRAME_DTMF;
-							dsp->f.subclass = dsp->thinkdigit;
-							FIX_INF(af);
-							if (chan)
-								ast_queue_frame(chan, af);
-							ast_frfree(af);
-						}
+					if ((dsp->thinkdigit != 'x') && (dsp->thinkdigit != digit)) {
+						/* If we found a digit, and we're changing digits, go
+						   ahead and send this one, but DON'T stop confmute because
+						   we're detecting something else, too... */
+						memset(&dsp->f, 0, sizeof(dsp->f));
+						dsp->f.frametype = AST_FRAME_DTMF_END;
+						dsp->f.subclass = dsp->thinkdigit;
+						FIX_INF(af);
+						if (chan)
+							ast_queue_frame(chan, af);
+						ast_frfree(af);
+					} else {
 						dsp->thinkdigit = digit;
-						return &dsp->f;
-					}
-					dsp->thinkdigit = digit;
-				} else {
-					if (dsp->thinkdigit) {
 						memset(&dsp->f, 0, sizeof(dsp->f));
-						if (dsp->thinkdigit != 'x') {
-							/* If we found a digit, send it now */
-							dsp->f.frametype = AST_FRAME_DTMF;
-							dsp->f.subclass = dsp->thinkdigit;
-							dsp->thinkdigit = 0;
-						} else {
-							dsp->f.frametype = AST_FRAME_DTMF;
-							dsp->f.subclass = 'u';
-							dsp->thinkdigit = 0;
-						}
+						dsp->f.frametype = AST_FRAME_DTMF_BEGIN;
+						dsp->f.subclass = dsp->thinkdigit;
 						FIX_INF(af);
 						if (chan)
 							ast_queue_frame(chan, af);
 						ast_frfree(af);
-						return &dsp->f;
 					}
+					return &dsp->f;
+				} else {
+					memset(&dsp->f, 0, sizeof(dsp->f));
+					if (dsp->thinkdigit != 'x') {
+						/* If we found a digit, send it now */
+						dsp->f.frametype = AST_FRAME_DTMF_END;
+						dsp->f.subclass = dsp->thinkdigit;
+						dsp->thinkdigit = 0;
+					} else {
+						dsp->f.frametype = AST_FRAME_DTMF;
+						dsp->f.subclass = 'u';
+						dsp->thinkdigit = 0;
+					}
+					FIX_INF(af);
+					if (chan)
+						ast_queue_frame(chan, af);
+					ast_frfree(af);
+					return &dsp->f;
 				}
 			}
 		} else if (!digit) {
@@ -1553,7 +1556,7 @@ struct ast_frame *ast_dsp_process(struct ast_channel *chan, struct ast_dsp *dsp,
 			} else {
 				if (dsp->td.dtmf.current_digits) {
 					memset(&dsp->f, 0, sizeof(dsp->f));
-					dsp->f.frametype = AST_FRAME_DTMF;
+					dsp->f.frametype = AST_FRAME_DTMF_END;
 					dsp->f.subclass = dsp->td.dtmf.digits[0];
 					memmove(dsp->td.dtmf.digits, dsp->td.dtmf.digits + 1, dsp->td.dtmf.current_digits);
 					dsp->td.dtmf.current_digits--;
diff --git a/main/file.c b/main/file.c
index 365d233027..48c1660694 100644
--- a/main/file.c
+++ b/main/file.c
@@ -1029,7 +1029,8 @@ static int waitstream_core(struct ast_channel *c, const char *breakon,
 			if (!fr)
 				return -1;
 			switch(fr->frametype) {
-			case AST_FRAME_DTMF:
+			case AST_FRAME_DTMF_BEGIN:
+			case AST_FRAME_DTMF_END:
 				if (context) {
 					const char exten[2] = { fr->subclass, '\0' };
 					if (ast_exists_extension(c, context, exten, 1, c->cid.cid_num)) {
@@ -1065,8 +1066,10 @@ static int waitstream_core(struct ast_channel *c, const char *breakon,
 				/* Write audio if appropriate */
 				if (audiofd > -1)
 					write(audiofd, fr->data, fr->datalen);
+			default:
+				/* Ignore all others */
+				break;
 			}
-			/* Ignore all others */
 			ast_frfree(fr);
 		}
 		ast_sched_runq(c->sched);
diff --git a/main/rtp.c b/main/rtp.c
index 5703d71403..b53ad405bb 100644
--- a/main/rtp.c
+++ b/main/rtp.c
@@ -106,14 +106,12 @@ struct rtpPayloadType {
 /*! \brief RTP session description */
 struct ast_rtp {
 	int s;
-	char resp;
 	struct ast_frame f;
 	unsigned char rawdata[8192 + AST_FRIENDLY_OFFSET];
 	unsigned int ssrc;		/*!< Synchronization source, RFC 3550, page 10. */
 	unsigned int themssrc;		/*!< Their SSRC */
 	unsigned int rxssrc;
 	unsigned int lastts;
-	unsigned int lastdigitts;
 	unsigned int lastrxts;
 	unsigned int lastividtimestamp;
 	unsigned int lastovidtimestamp;
@@ -128,11 +126,17 @@ struct ast_rtp {
 	unsigned int cycles;            /*!< Shifted count of sequence number cycles */
 	double rxjitter;                /*!< Interarrival jitter at the moment */
 	double rxtransit;               /*!< Relative transit time for previous packet */
-	unsigned int lasteventendseqn;
 	int lasttxformat;
 	int lastrxformat;
+	/* DTMF Reception Variables */
+	char resp;
+	unsigned int lasteventendseqn;
 	int dtmfcount;
 	unsigned int dtmfduration;
+	/* DTMF Transmission Variables */
+	unsigned int lastdigitts;
+	char send_digit;
+	int send_payload;
 	int nat;
 	unsigned int flags;
 	struct sockaddr_in us;		/*!< Socket representation of the local endpoint. */
@@ -164,6 +168,8 @@ static void timeval2ntp(struct timeval tv, unsigned int *msw, unsigned int *lsw)
 static int ast_rtcp_write_sr(void *data);
 static int ast_rtcp_write_rr(void *data);
 static unsigned int ast_rtcp_calc_interval(struct ast_rtp *rtp);
+static int ast_rtp_senddigit_continuation(struct ast_rtp *rtp);
+int ast_rtp_senddigit_end(struct ast_rtp *rtp, char digit);
 static int bridge_p2p_rtcp_write(struct ast_rtp *rtp, unsigned int *rtcpheader, int len);
 
 #define FLAG_3389_WARNING		(1 << 0)
@@ -174,6 +180,7 @@ static int bridge_p2p_rtcp_write(struct ast_rtp *rtp, unsigned int *rtcpheader,
 #define FLAG_P2P_SENT_MARK              (1 << 4)
 #define FLAG_P2P_NEED_DTMF              (1 << 5)
 #define FLAG_CALLBACK_MODE              (1 << 6)
+#define FLAG_DTMF_COMPENSATE            (1 << 7)
 
 /*!
  * \brief Structure defining an RTCP session.
@@ -531,7 +538,12 @@ void ast_rtp_setdtmf(struct ast_rtp *rtp, int dtmf)
 	ast_set2_flag(rtp, dtmf ? 1 : 0, FLAG_HAS_DTMF);
 }
 
-static struct ast_frame *send_dtmf(struct ast_rtp *rtp)
+void ast_rtp_setdtmfcompensate(struct ast_rtp *rtp, int compensate)
+{
+	ast_set2_flag(rtp, compensate ? 1 : 0, FLAG_DTMF_COMPENSATE);
+}
+
+static struct ast_frame *send_dtmf(struct ast_rtp *rtp, enum ast_frame_type type)
 {
 	if (ast_tvcmp(ast_tvnow(), rtp->dtmfmute) < 0) {
 		if (option_debug)
@@ -546,15 +558,13 @@ static struct ast_frame *send_dtmf(struct ast_rtp *rtp)
 		rtp->f.frametype = AST_FRAME_CONTROL;
 		rtp->f.subclass = AST_CONTROL_FLASH;
 	} else {
-		rtp->f.frametype = AST_FRAME_DTMF;
+		rtp->f.frametype = type;
 		rtp->f.subclass = rtp->resp;
 	}
 	rtp->f.datalen = 0;
 	rtp->f.samples = 0;
 	rtp->f.mallocd = 0;
 	rtp->f.src = "RTP";
-	rtp->resp = 0;
-	rtp->dtmfduration = 0;
 	return &rtp->f;
 	
 }
@@ -607,7 +617,7 @@ static struct ast_frame *process_cisco_dtmf(struct ast_rtp *rtp, unsigned char *
 		resp = 'X';
 	}
 	if (rtp->resp && (rtp->resp != resp)) {
-		f = send_dtmf(rtp);
+		f = send_dtmf(rtp, AST_FRAME_DTMF_END);
 	}
 	rtp->resp = resp;
 	rtp->dtmfcount = dtmftimeout;
@@ -633,6 +643,7 @@ static struct ast_frame *process_rfc2833(struct ast_rtp *rtp, unsigned char *dat
 	char resp = 0;
 	struct ast_frame *f = NULL;
 
+	/* Figure out event, event end, and duration */
 	event = ntohl(*((unsigned int *)(data)));
 	event >>= 24;
 	event_end = ntohl(*((unsigned int *)(data)));
@@ -640,8 +651,12 @@ static struct ast_frame *process_rfc2833(struct ast_rtp *rtp, unsigned char *dat
 	event_end >>= 24;
 	duration = ntohl(*((unsigned int *)(data)));
 	duration &= 0xFFFF;
+
+	/* Print out debug if turned on */
 	if (rtpdebug || option_debug > 2)
 		ast_log(LOG_DEBUG, "- RTP 2833 Event: %08x (len = %d)\n", event, len);
+
+	/* Figure out what digit was pressed */
 	if (event < 10) {
 		resp = '0' + event;
 	} else if (event < 11) {
@@ -653,25 +668,21 @@ static struct ast_frame *process_rfc2833(struct ast_rtp *rtp, unsigned char *dat
 	} else if (event < 17) {	/* Event 16: Hook flash */
 		resp = 'X';	
 	}
-	if (rtp->resp && (rtp->resp != resp)) {
-		f = send_dtmf(rtp);
-	} else if (event_end & 0x80) {
-		if (rtp->resp) {
-			if (rtp->lasteventendseqn != seqno) {
-				f = send_dtmf(rtp);
-				rtp->lasteventendseqn = seqno;
-			}
-			rtp->resp = 0;
-		}
-		resp = 0;
-		duration = 0;
-	} else if (rtp->resp && rtp->dtmfduration && (duration < rtp->dtmfduration)) {
-		f = send_dtmf(rtp);
-	}
-	if (!(event_end & 0x80))
+
+	if ((!(rtp->resp) && (!(event_end & 0x80))) || (rtp->resp && rtp->resp != resp)) {
 		rtp->resp = resp;
+		if (!ast_test_flag(rtp, FLAG_DTMF_COMPENSATE))
+			f = send_dtmf(rtp, AST_FRAME_DTMF_BEGIN);
+	} else if (event_end & 0x80 && rtp->lasteventendseqn != seqno && rtp->resp) {
+		f = send_dtmf(rtp, AST_FRAME_DTMF_END);
+		f->samples = duration;
+		rtp->resp = 0;
+		rtp->lasteventendseqn = seqno;
+	}
+
 	rtp->dtmfcount = dtmftimeout;
 	rtp->dtmfduration = duration;
+
 	return f;
 }
 
@@ -1030,6 +1041,10 @@ struct ast_frame *ast_rtp_read(struct ast_rtp *rtp)
 	unsigned int *rtpheader;
 	struct rtpPayloadType rtpPT;
 	
+	/* If time is up, kill it */
+	if (rtp->send_digit)
+		ast_rtp_senddigit_continuation(rtp);
+
 	len = sizeof(sin);
 	
 	/* Cache where the header will go */
@@ -1172,13 +1187,13 @@ struct ast_frame *ast_rtp_read(struct ast_rtp *rtp)
 				duration &= 0xFFFF;
 				ast_verbose("Got  RTP RFC2833 from   %s:%d (type %-2.2d, seq %-6.6u, ts %-6.6u, len %-6.6u, mark %d, event %08x, end %d, duration %-5.5d) \n", ast_inet_ntoa(sin.sin_addr), ntohs(sin.sin_port), payloadtype, seqno, timestamp, res - hdrlen, (mark?1:0), event, ((event_end & 0x80)?1:0), duration);
 			}
-			if (rtp->lasteventseqn <= seqno || rtp->resp == 0 || (rtp->lasteventseqn >= 65530 && seqno <= 6)) {
+			if (rtp->lasteventseqn <= seqno || (rtp->lasteventseqn >= 65530 && seqno <= 6)) {
 				f = process_rfc2833(rtp, rtp->rawdata + AST_FRIENDLY_OFFSET + hdrlen, res - hdrlen, seqno);
 				rtp->lasteventseqn = seqno;
 			}
 		} else if (rtpPT.code == AST_RTP_CISCO_DTMF) {
 			/* It's really special -- process it the Cisco way */
-			if (rtp->lasteventseqn <= seqno || rtp->resp == 0 || (rtp->lasteventseqn >= 65530 && seqno <= 6)) {
+			if (rtp->lasteventseqn <= seqno || (rtp->lasteventseqn >= 65530 && seqno <= 6)) {
 				f = process_cisco_dtmf(rtp, rtp->rawdata + AST_FRIENDLY_OFFSET + hdrlen, res - hdrlen);
 				rtp->lasteventseqn = seqno;
 			}
@@ -1198,26 +1213,9 @@ struct ast_frame *ast_rtp_read(struct ast_rtp *rtp)
 
 	rtp->rxseqno = seqno;
 
-	if (rtp->dtmfcount) {
-#if 0
-		printf("dtmfcount was %d\n", rtp->dtmfcount);
-#endif		
-		rtp->dtmfcount -= (timestamp - rtp->lastrxts);
-		if (rtp->dtmfcount < 0)
-			rtp->dtmfcount = 0;
-#if 0
-		if (dtmftimeout != rtp->dtmfcount)
-			printf("dtmfcount is %d\n", rtp->dtmfcount);
-#endif
-	}
+	/* Record received timestamp as last received now */
 	rtp->lastrxts = timestamp;
 
-	/* Send any pending DTMF */
-	if (rtp->resp && !rtp->dtmfcount) {
-		if (option_debug)
-			ast_log(LOG_DEBUG, "Sending pending DTMF\n");
-		return send_dtmf(rtp);
-	}
 	rtp->f.mallocd = 0;
 	rtp->f.datalen = res - hdrlen;
 	rtp->f.data = rtp->rawdata + hdrlen + AST_FRIENDLY_OFFSET;
@@ -1962,35 +1960,42 @@ static unsigned int calc_txstamp(struct ast_rtp *rtp, struct timeval *delivery)
 	return (unsigned int) ms;
 }
 
-int ast_rtp_senddigit(struct ast_rtp *rtp, char digit)
+/* Convert DTMF digit into something usable */
+static int digit_convert(char digit)
+{
+	if ((digit <= '9') && (digit >= '0'))
+                digit -= '0';
+        else if (digit == '*')
+                digit = 10;
+        else if (digit == '#')
+                digit = 11;
+        else if ((digit >= 'A') && (digit <= 'D'))
+                digit = digit - 'A' + 12;
+        else if ((digit >= 'a') && (digit <= 'd'))
+                digit = digit - 'a' + 12;
+        else {
+                ast_log(LOG_WARNING, "Don't know how to represent '%c'\n", digit);
+                return -1;
+        }
+	return 0;
+}
+
+/*! \brief Send begin frames for DTMF */
+int ast_rtp_senddigit_begin(struct ast_rtp *rtp, char digit)
 {
 	unsigned int *rtpheader;
-	int hdrlen = 12;
-	int res;
-	int x;
-	int payload;
+	int hdrlen = 12, res = 0, i = 0, payload = 0;
 	char data[256];
 
-	if ((digit <= '9') && (digit >= '0'))
-		digit -= '0';
-	else if (digit == '*')
-		digit = 10;
-	else if (digit == '#')
-		digit = 11;
-	else if ((digit >= 'A') && (digit <= 'D')) 
-		digit = digit - 'A' + 12;
-	else if ((digit >= 'a') && (digit <= 'd')) 
-		digit = digit - 'a' + 12;
-	else {
-		ast_log(LOG_WARNING, "Don't know how to represent '%c'\n", digit);
+	if (digit_convert(digit))
 		return -1;
-	}
-	payload = ast_rtp_lookup_code(rtp, 0, AST_RTP_DTMF);
 
 	/* If we have no peer, return immediately */	
-	if (!rtp->them.sin_addr.s_addr)
+	if (!rtp->them.sin_addr.s_addr || !rtp->them.sin_port)
 		return 0;
 
+	payload = ast_rtp_lookup_code(rtp, 0, AST_RTP_DTMF);
+
 	rtp->dtmfmute = ast_tvadd(ast_tvnow(), ast_tv(0, 500000));
 	
 	/* Get a pointer to the header */
@@ -1999,51 +2004,111 @@ int ast_rtp_senddigit(struct ast_rtp *rtp, char digit)
 	rtpheader[1] = htonl(rtp->lastdigitts);
 	rtpheader[2] = htonl(rtp->ssrc); 
 	rtpheader[3] = htonl((digit << 24) | (0xa << 16) | (0));
-	for (x = 0; x < 6; x++) {
-		if (rtp->them.sin_port && rtp->them.sin_addr.s_addr) {
-			res = sendto(rtp->s, (void *) rtpheader, hdrlen + 4, 0, (struct sockaddr *) &rtp->them, sizeof(rtp->them));
-			if (res < 0) 
-				ast_log(LOG_ERROR, "RTP Transmission error to %s:%d: %s\n",
-					ast_inet_ntoa(rtp->them.sin_addr),
-					ntohs(rtp->them.sin_port), strerror(errno));
-			if (rtp_debug_test_addr(&rtp->them))
-				ast_verbose("Sent RTP DTMF packet to %s:%d (type %-2.2d, seq %-6.6u, ts %-6.6u, len %-6.6u)\n",
-					    ast_inet_ntoa(rtp->them.sin_addr),
-					    ntohs(rtp->them.sin_port), payload, rtp->seqno, rtp->lastdigitts, res - hdrlen);
-		}
-		/* Sequence number of last two end packets does not get incremented */
-		if (x < 3)
-			rtp->seqno++;
+
+	for (i = 0; i < 2; i++) {
+		res = sendto(rtp->s, (void *) rtpheader, hdrlen + 4, 0, (struct sockaddr *) &rtp->them, sizeof(rtp->them));
+		if (res < 0) 
+			ast_log(LOG_ERROR, "RTP Transmission error to %s:%d: %s\n",
+				ast_inet_ntoa(rtp->them.sin_addr),
+				ntohs(rtp->them.sin_port), strerror(errno));
+		if (rtp_debug_test_addr(&rtp->them))
+			ast_verbose("Sent RTP DTMF packet to %s:%d (type %-2.2d, seq %-6.6u, ts %-6.6u, len %-6.6u)\n",
+				    ast_inet_ntoa(rtp->them.sin_addr),
+				    ntohs(rtp->them.sin_port), payload, rtp->seqno, rtp->lastdigitts, res - hdrlen);
+		/* Increment sequence number */
+		rtp->seqno++;
 		/* Clear marker bit and set seqno */
 		rtpheader[0] = htonl((2 << 30) | (payload << 16) | (rtp->seqno));
-		/* For the last three packets, set the duration and the end bit */
-		if (x == 2) {
-#if 0
-			/* No, this is wrong...  Do not increment lastdigitts, that's not according
-			   to the RFC, as best we can determine */
-			rtp->lastdigitts++; /* or else the SPA3000 will click instead of beeping... */
-			rtpheader[1] = htonl(rtp->lastdigitts);
-#endif			
-			/* Make duration 800 (100ms) */
-			rtpheader[3] |= htonl((800));
-			/* Set the End bit */
-			rtpheader[3] |= htonl((1 << 23));
-		}
 	}
-	/*! \note Increment the digit timestamp by 120ms, to ensure that digits
-	   sent sequentially with no intervening non-digit packets do not
-	   get sent with the same timestamp, and that sequential digits
-	   have some 'dead air' in between them
-	*/
-	rtp->lastdigitts += 960;
-	/* Increment the sequence number to reflect the last packet
-	   that was sent
-	*/
+
+	/* Since we received a begin, we can safely store the digit and disable any compensation */
+	rtp->send_digit = digit;
+	rtp->send_payload = payload;
+
+	return 0;
+}
+
+/*! \brief Send continuation frame for DTMF */
+static int ast_rtp_senddigit_continuation(struct ast_rtp *rtp)
+{
+	unsigned int *rtpheader;
+	int hdrlen = 12, res = 0;
+	char data[256];
+
+	if (!rtp->them.sin_addr.s_addr || !rtp->them.sin_port)
+		return 0;
+
+	/* Setup packet to send */
+	rtpheader = (unsigned int *)data;
+        rtpheader[0] = htonl((2 << 30) | (1 << 23) | (rtp->send_payload << 16) | (rtp->seqno));
+        rtpheader[1] = htonl(rtp->lastdigitts);
+        rtpheader[2] = htonl(rtp->ssrc);
+        rtpheader[3] = htonl((rtp->send_digit << 24) | (0xa << 16) | (0));
+	rtpheader[0] = htonl((2 << 30) | (rtp->send_payload << 16) | (rtp->seqno));
+	
+	/* Transmit */
+	res = sendto(rtp->s, (void *) rtpheader, hdrlen + 4, 0, (struct sockaddr *) &rtp->them, sizeof(rtp->them));
+	if (res < 0)
+		ast_log(LOG_ERROR, "RTP Transmission error to %s:%d: %s\n",
+			ast_inet_ntoa(rtp->them.sin_addr),
+			ntohs(rtp->them.sin_port), strerror(errno));
+	if (rtp_debug_test_addr(&rtp->them))
+		ast_verbose("Sent RTP DTMF packet to %s:%d (type %-2.2d, seq %-6.6u, ts %-6.6u, len %-6.6u)\n",
+			    ast_inet_ntoa(rtp->them.sin_addr),
+			    ntohs(rtp->them.sin_port), rtp->send_payload, rtp->seqno, rtp->lastdigitts, res - hdrlen);
+
+	/* Increment sequence number */
 	rtp->seqno++;
+
 	return 0;
 }
 
-/* \brief Public function: Send an H.261 fast update request, some devices need this rather than SIP XML */
+/*! \brief Send end packets for DTMF */
+int ast_rtp_senddigit_end(struct ast_rtp *rtp, char digit)
+{
+	unsigned int *rtpheader;
+	int hdrlen = 12, res = 0, i = 0;
+	char data[256];
+	
+	/* If no address, then bail out */
+	if (!rtp->them.sin_addr.s_addr || !rtp->them.sin_port)
+		return 0;
+	
+	/* Convert our digit to the crazy RTP way */
+	if (digit_convert(digit))
+		return -1;
+
+	rtpheader = (unsigned int *)data;
+	rtpheader[0] = htonl((2 << 30) | (1 << 23) | (rtp->send_payload << 16) | (rtp->seqno));
+	rtpheader[1] = htonl(rtp->lastdigitts);
+	rtpheader[2] = htonl(rtp->ssrc);
+	rtpheader[3] = htonl((digit << 24) | (0xa << 16) | (0));
+	/* Send duration to 100ms */
+	rtpheader[3] |= htonl((800));
+	/* Set end bit */
+	rtpheader[3] |= htonl((1 << 23));
+	rtpheader[0] = htonl((2 << 30) | (rtp->send_payload << 16) | (rtp->seqno));
+	/* Send 3 termination packets */
+	for (i = 0; i < 3; i++) {
+		res = sendto(rtp->s, (void *) rtpheader, hdrlen + 4, 0, (struct sockaddr *) &rtp->them, sizeof(rtp->them));
+		if (res < 0)
+			ast_log(LOG_ERROR, "RTP Transmission error to %s:%d: %s\n",
+				ast_inet_ntoa(rtp->them.sin_addr),
+				ntohs(rtp->them.sin_port), strerror(errno));
+		if (rtp_debug_test_addr(&rtp->them))
+			ast_verbose("Sent RTP DTMF packet to %s:%d (type %-2.2d, seq %-6.6u, ts %-6.6u, len %-6.6u)\n",
+				    ast_inet_ntoa(rtp->them.sin_addr),
+				    ntohs(rtp->them.sin_port), rtp->send_payload, rtp->seqno, rtp->lastdigitts, res - hdrlen);
+	}
+	rtp->send_digit = 0;
+	/* Increment lastdigitts */
+	rtp->lastdigitts += 960;
+	rtp->seqno++;
+
+	return res;
+}
+
+/*! \brief Public function: Send an H.261 fast update request, some devices need this rather than SIP XML */
 int ast_rtcp_send_h261fur(void *data)
 {
 	struct ast_rtp *rtp = data;
diff --git a/res/res_agi.c b/res/res_agi.c
index a050d1c8da..bf51946f9f 100644
--- a/res/res_agi.c
+++ b/res/res_agi.c
@@ -1016,6 +1016,8 @@ static int handle_recordfile(struct ast_channel *chan, AGI *agi, int argc, char
 				break;
 			case AST_FRAME_VIDEO:
 				ast_writestream(fs, f);
+			default:
+				/* Ignore all other frames */
 				break;
 			}
 			ast_frfree(f);
diff --git a/res/res_features.c b/res/res_features.c
index 10faf00270..aba923fb8b 100644
--- a/res/res_features.c
+++ b/res/res_features.c
@@ -1439,9 +1439,9 @@ int ast_bridge_call(struct ast_channel *chan,struct ast_channel *peer,struct ast
 				if (aoh && aoh->flag == AST_OPTION_FLAG_REQUEST)
 					ast_channel_setoption(other, ntohs(aoh->option), aoh->data, f->datalen - sizeof(struct ast_option_header), 0);
 			}
-		}
-		/* check for '*', if we find it it's time to disconnect */
-		if (f->frametype == AST_FRAME_DTMF) {
+		} else if (f->frametype == AST_FRAME_DTMF_BEGIN) {
+			/* eat it */
+		} else if (f->frametype == AST_FRAME_DTMF) {
 			char *featurecode;
 			int sense;
 
-- 
GitLab