From d5303e463f055ce1d398f7c58c6af717c831f34f Mon Sep 17 00:00:00 2001
From: Yalu Zhang <yalu.zhang@iopsys.eu>
Date: Thu, 11 Jul 2024 13:04:58 +0000
Subject: [PATCH] Queue DTMF frames to the channel if the call is terminated by
 Asterisk core or application

Forwarding RTP telephone event packets transparently to the network caused a regression that DTMF digits can't be read from ast_channel if the call is terminated by Asterisk core or applications, e.g. VoiceMailMain, call forwarding feature access codes, and etc. Now with this fix, DTMF frames will be queued to ast_channel simultaneously forwarding those RTP telephone event packets transparently.

Also revert 44071bb7fad261f5370af12fb5b3ec01e3a6612c. "Fix a regression that is introduced by RTP events generated by DSP"
---
 src/channels/chan_voicemngr.c | 44 ++++++++++++++++++-----------------
 src/channels/chan_voicemngr.h |  1 -
 2 files changed, 23 insertions(+), 22 deletions(-)

diff --git a/src/channels/chan_voicemngr.c b/src/channels/chan_voicemngr.c
index 9f20edd..e358744 100644
--- a/src/channels/chan_voicemngr.c
+++ b/src/channels/chan_voicemngr.c
@@ -2602,12 +2602,11 @@ void handle_dtmf_calling(struct chan_voicemngr_subchannel *sub)
 	char dtmf_last_char = p->dtmfbuf[(dtmfbuf_len - 1)];
 	char dtmf_one_before_last_char = p->dtmfbuf[(dtmfbuf_len > 1 ? dtmfbuf_len - 2 : 0)];
 
-	sub->direct_extension = 0;
-	if (ast_exists_extension(NULL, p->context_direct, p->dtmfbuf, 1, p->cid_num) && !ast_matchmore_extension(NULL, p->context_direct, p->dtmfbuf, 1, p->cid_num))
+	if (ast_exists_extension(NULL, p->context_direct, p->dtmfbuf, 1, p->cid_num) &&
+			!ast_matchmore_extension(NULL, p->context_direct, p->dtmfbuf, 1, p->cid_num))
 	{
 		//We have a full match in the "direct" context, so have asterisk place a call immediately
 		ast_debug(9, "Direct extension matching %s found\n", p->dtmfbuf);
-		sub->direct_extension = 1;
 		chan_voicemngr_start_calling(sub, p->context_direct);
 	}
 
@@ -3138,17 +3137,20 @@ static void handle_dtmf(enum LINE_EVENT event,
 	gettimeofday(&tim, NULL);
 	p = sub->parent;
 
-	if (p->dtmf_first < 0) {
+	if (p->dtmf_first < 0) { // DTMF pressed
 		p->dtmf_first = dtmf_button;
 		ast_debug(9,"Pressed DTMF %s\n", dtmfMap->name);
-		if (owner && chan_voicemngr_should_relay_dtmf(sub) && (sub->dtmf_mode == AST_SIP_DTMF_INFO || sub->conference_initiator == 1 || sub->direct_extension == 1)) {
-			// INCALL
+		/* Frame AST_FRAME_DTMF_BEGIN must be queued to the channel if the call is terminated by Asterisk core or applications,
+		 * e.g. VoiceMailMain, call forwarding feature access codes. Otherwise DTMF digits won't be collected if DTMF relay method
+		 * is not SIP INFO.
+		 * There is no bridge created for those calls. A bridge is always created for normal 2-way or 3-way calls no matter they
+		 * are internal or external. */
+		if (owner && chan_voicemngr_should_relay_dtmf(sub) &&
+				(sub->dtmf_mode == AST_SIP_DTMF_INFO || sub->conference_initiator == 1 || !ast_channel_internal_bridge(owner))) {
 			send_outgoing_dtmf(owner, dtmf_button, AST_FRAME_DTMF_BEGIN);
 		}
-
 		/* Do not send AST_FRAME_DTMF_BEGIN to allow DSP-generated tone to pass through */
-	}
-	else if (p->dtmf_first == dtmf_button) {
+	} else if (p->dtmf_first == dtmf_button) { // DTMF released
 		ast_log(LOG_NOTICE,"Released DTMF %s\n", dtmfMap->name);
 		//ast_log(LOG_DTMF, "Detected DTMF %c, line %d\n", dtmfMap->c, sub->parent->line_id);
 
@@ -3179,8 +3181,16 @@ static void handle_dtmf(enum LINE_EVENT event,
 			if (sub->channel_state == OFFHOOK) {
 				sub->channel_state = DIALING;
 				//ast_debug(5,"Set to state %s.\n", state2str(sub->channel_state));
-			}
-			else if (sub->channel_state != INCALL) {
+			} else if (sub->channel_state == INCALL) {
+				/* Frame AST_FRAME_DTMF_END must be queued to the channel if the call is terminated by Asterisk core or applications,
+				 * e.g. VoiceMailMain, call forwarding feature access codes. Otherwise DTMF digits won't be collected if DTMF relay method
+				 * is not SIP INFO.
+				 * There is no bridge created for those calls. A bridge is always created for normal 2-way or 3-way calls no matter they
+				 * are internal or external. */
+				if (owner && (sub->dtmf_mode == AST_SIP_DTMF_INFO || sub->conference_initiator == 1 || !ast_channel_internal_bridge(owner))) {
+					send_outgoing_dtmf(owner, dtmf_button, AST_FRAME_DTMF_END);
+				}
+			} else {
 				struct ast_frame f = { 0, };
 				f.subclass.integer = dtmf_button;
 				f.src = "TELCHAN";
@@ -3192,15 +3202,9 @@ static void handle_dtmf(enum LINE_EVENT event,
 					//ast_debug(5,"DTMF %s queuing frame.\n", dtmfMap->name);
 					ast_queue_frame(owner, &f);
 				}
-			} else {
-				if (owner && (sub->dtmf_mode == AST_SIP_DTMF_INFO || sub->conference_initiator == 1 || sub->direct_extension)) {
-					// INCALL
-					send_outgoing_dtmf(owner, dtmf_button, AST_FRAME_DTMF_END);
-				}
 			}
-		}
-	}
-	else {
+		} // end of if (p->hf_detected)
+	} else {
 		p->dtmf_first = -1;
 	}
 }
@@ -3897,7 +3901,6 @@ static struct chan_voicemngr_pvt *chan_voicemngr_allocate_pvt(void)
 				sub->sip_client_id = -1;
 				sub->period = default_ptime;												// 20 ms
 				sub->dtmf_mode = AST_SIP_DTMF_RFC_4733;
-				sub->direct_extension = 0;
 				sub->dtmf_pt = RTP_PT_DTMF;
 				sub->conference_initiator = 0;
 				tmp->sub[i] = sub;
@@ -5780,7 +5783,6 @@ static int chan_voicemngr_close_connection(struct chan_voicemngr_subchannel *sub
 		sub->codec = -1;
 		sub->call_id = CALLID_INVALID;
 		sub->updated_codec = 0;
-		sub->direct_extension = 0;
 		ast_debug(1, "Virtual Asterisk connection %d/%d destroyed\n", p->line_id, sub->connection_id);
 	}
 
diff --git a/src/channels/chan_voicemngr.h b/src/channels/chan_voicemngr.h
index 4ad5e06..304761d 100644
--- a/src/channels/chan_voicemngr.h
+++ b/src/channels/chan_voicemngr.h
@@ -190,7 +190,6 @@ struct chan_voicemngr_subchannel {
 	int updated_codec;
 	int sip_client_id;	/* The SIP client used for the current call: -1 = not set, 0 = internal call, 1..MAX_SIP_CLIENTS = sip client id*/
 	int congestion_timer_id;
-	int direct_extension;
 };
 
 struct chan_voicemngr_channel_tech {
-- 
GitLab