From 5f155d0a6acf92db65739633ef7d2fa7b9381e9e Mon Sep 17 00:00:00 2001
From: Yalu Zhang <yalu.zhang@iopsys.eu>
Date: Thu, 2 May 2024 14:51:31 +0000
Subject: [PATCH] Generate call waiting tone by Asterisk instead of by DSP

Type II caller ID presentation during call waiting can be disrupted when call waiting
tone is played also by DSP. So call waiting tone now is generated by Asterisk core by
playing back the audio file.
---
 src/channels/chan_voicemngr.c | 97 ++++++++++++++++++++---------------
 1 file changed, 57 insertions(+), 40 deletions(-)

diff --git a/src/channels/chan_voicemngr.c b/src/channels/chan_voicemngr.c
index 196913f..9a86dcd 100644
--- a/src/channels/chan_voicemngr.c
+++ b/src/channels/chan_voicemngr.c
@@ -119,8 +119,6 @@ static int chan_voicemngr_signal_ringing(struct chan_voicemngr_pvt *p);
 static int chan_voicemngr_stop_ringing(struct chan_voicemngr_pvt *p);
 static int chan_voicemngr_signal_ringing_callerid_pending(struct chan_voicemngr_pvt *p);
 static int chan_voicemngr_stop_ringing_callerid_pending(struct chan_voicemngr_pvt *p);
-static int chan_voicemngr_signal_callwaiting(const struct chan_voicemngr_pvt *p);
-static int chan_voicemngr_stop_callwaiting(const struct chan_voicemngr_pvt *p);
 static int chan_voicemngr_signal_callerid(struct ast_channel *chan, struct chan_voicemngr_subchannel *sub, int callwt);
 static struct chan_voicemngr_subchannel *chan_voicemngr_get_idle_subchannel(const struct chan_voicemngr_pvt *p);
 static struct chan_voicemngr_subchannel* chan_voicemngr_get_active_subchannel(const struct chan_voicemngr_pvt *p);
@@ -130,8 +128,8 @@ static struct chan_voicemngr_pvt* chan_voicemngr_get_pvt_from_lineid(struct chan
 static void handle_dtmf_calling(struct chan_voicemngr_subchannel *sub);
 static void chan_voicemngr_cancel_dialing_timeouts(struct chan_voicemngr_pvt *p);
 static int chan_voicemngr_should_relay_dtmf(const struct chan_voicemngr_subchannel *sub);
-static struct ast_channel *chan_voicemngr_new(struct chan_voicemngr_subchannel *subchan, int state, const char *ext, const char *context,
-		const struct ast_assigned_ids *assignedids, const struct ast_channel *requestor,
+static struct ast_channel *chan_voicemngr_new(struct chan_voicemngr_subchannel *subchan, int state, const char *ext,
+		const char *context, const struct ast_assigned_ids *assignedids, const struct ast_channel *requestor,
 		struct ast_format_cap *format);
 static int handle_dialtone_timeout(const void *data);
 static int handle_congestion_timeout(const void *data);
@@ -1402,8 +1400,10 @@ static int chan_voicemngr_call(struct ast_channel *chan, const char *dest, int t
 		chan_voicemngr_send_ubus_event("USER_BUSY",p->line_id);
 		ast_queue_control(chan, AST_CONTROL_BUSY);
 	}
-	else if (chan_voicemngr_in_call(p) &&                          		// a call is established
-		is_call_waiting_enabled(p->context)) {			// call waiting enabled
+	else if (chan_voicemngr_in_call(p) && // a call is established
+		is_call_waiting_enabled(p->context)) { // call waiting enabled
+		int interval_ms = cw_tone_interval * 1000;
+
 		ast_debug(1, "Call waiting\n");
 		sub->channel_state = CALLWAITING;
 		// During call waiting, display caller ID for the second incoming call if CLIP is enabled or
@@ -1411,8 +1411,8 @@ static int chan_voicemngr_call(struct ast_channel *chan, const char *dest, int t
 		if (channel_config[p->line_id].calleridenable) {
 			p->tech->signal_ringing_callerid_pending(p);
 			p->tech->signal_callerid(chan, sub, 0);
+			sub->cw_tone_timer_id = ast_sched_add(sched, interval_ms, call_waiting_cb, sub);
 		} else {
-			int interval_ms = cw_tone_interval * 1000;
 			p->tech->signal_callerid(chan, sub, 1);
 			sub->cw_tone_timer_id = ast_sched_add(sched, interval_ms, call_waiting_cb, sub);
 		}
@@ -1482,6 +1482,39 @@ static int chan_voicemngr_call(struct ast_channel *chan, const char *dest, int t
 	return 0;
 }
 
+static int chan_voicemngr_signal_callwaiting(const struct chan_voicemngr_subchannel *sub)
+{
+	/*
+	 * Play beep by Asterisk on the first call's channel since a bridge needs to be established
+	 * for playing an audio file.
+	 */
+	struct chan_voicemngr_subchannel *sub_peer = chan_voicemngr_subchannel_get_peer(sub);
+	if (sub_peer->owner) {
+		struct ast_bridge_channel *play_bridge_channel = ast_channel_get_bridge_channel(sub_peer->owner);
+
+		if (play_bridge_channel) {
+			ast_bridge_channel_queue_playfile(play_bridge_channel, NULL, "beep", NULL);
+			ao2_ref(play_bridge_channel, -1);
+			return 0;
+		} else {
+			ast_log(LOG_ERROR, "The bridge channel has not been created yet\n");
+		}
+	}
+
+	return -1;
+}
+
+static int chan_voicemngr_stop_callwaiting(const struct chan_voicemngr_subchannel *sub)
+{
+	/*
+	 * Nothing needs to be done for ast_bridge_channel_queue_playfile() since it will stop by itself when
+	 * the audio file is played.
+	 */
+	chan_voicemngr_send_ubus_event("CALLWAITING_STOPPED", sub->parent->line_id);
+
+	return 0;
+}
+
 static int chan_voicemngr_hangup(struct ast_channel *ast)
 {
 	struct chan_voicemngr_pvt *p;
@@ -1534,7 +1567,7 @@ static int chan_voicemngr_hangup(struct ast_channel *ast)
 	}
 
 	if (sub->channel_state == CALLWAITING) {
-		chan_voicemngr_stop_callwaiting(p);
+		chan_voicemngr_stop_callwaiting(sub);
 	} else if (sub_peer->channel_state == ONHOLD &&
 			sub_peer->onhold_hangup_timer_id != -1 && sub->channel_state == RINGING) {
 		ast_debug(2, "There is a forgotten onhold call, not releasing channel\n");
@@ -1773,13 +1806,13 @@ static void audio_rx_stream_handler(pe_stream_t *stream __attribute__((unused)),
 static int chan_voicemngr_write(struct ast_channel *ast, struct ast_frame *frame)
 {
 	struct chan_voicemngr_subchannel *sub = ast_channel_tech_pvt(ast);
+	enum ast_channel_state chan_state = ast_channel_state(ast);
 	int packet_size;
 	audio_packet_t *ap;
 	int sip_client_id = -1;
 
-	if (ast_channel_state(ast) != AST_STATE_UP && ast_channel_state(ast) != AST_STATE_RING) {
-		/* Silently ignore packets until channel is up */
-		ast_debug(5, "error: channel not up\n");
+	if (chan_state != AST_STATE_UP && chan_state != AST_STATE_RING && chan_state != AST_STATE_RINGING_CW) {
+		ast_debug(5, "Ignore the frame since channel state is %s\n", ast_state2str(chan_state));
 		return 0;
 	}
 
@@ -2268,7 +2301,7 @@ static int cwtimeout_cb(const void *data)
 	}
 	//ast_mutex_unlock(&sub->parent->lock);
 	if (sub->channel_state == CALLWAITING)
-		chan_voicemngr_stop_callwaiting(sub->parent);
+		chan_voicemngr_stop_callwaiting(sub);
 	else if (sub->channel_state == RINGING)
 		chan_voicemngr_stop_ringing(sub->parent);
 	pvt_unlock(sub->parent);
@@ -2290,13 +2323,10 @@ static int call_waiting_cb(const void *data)
 	struct chan_voicemngr_subchannel *sub = (struct chan_voicemngr_subchannel *) data;
 	int interval_ms = cw_tone_interval * 1000;
 
-	pvt_lock(sub->parent, "call waiting callback");
-	if (sub->parent) {
-		chan_voicemngr_signal_callwaiting(sub->parent);
+	if (sub) {
+		chan_voicemngr_signal_callwaiting(sub);
+		sub->cw_tone_timer_id = ast_sched_add(sched, interval_ms, call_waiting_cb, sub);
 	}
-	pvt_unlock(sub->parent);
-
-	sub->cw_tone_timer_id = ast_sched_add(sched, interval_ms, call_waiting_cb, sub);
 
 	return 0;
 }
@@ -2713,7 +2743,7 @@ static void handle_Rnumber_etsi(struct chan_voicemngr_subchannel *sub, struct ch
 				if (sub_peer->channel_state == CALLWAITING) {
 					ast_log(LOG_WARNING, "R1 call waiting\n");
 					/* Stop call waiting tone on current call */
-					chan_voicemngr_stop_callwaiting(p);
+					chan_voicemngr_stop_callwaiting(sub_peer);
 
 					if (ast_sched_del(sched, sub_peer->cw_timer_id)) {
 						ast_log(LOG_WARNING, "Failed to remove scheduled call waiting timer\n");
@@ -2754,7 +2784,7 @@ static void handle_Rnumber_etsi(struct chan_voicemngr_subchannel *sub, struct ch
 					ast_log(LOG_WARNING, "R2 Call waiting\n");
 
 					/* Stop call waiting tone on current call */
-					chan_voicemngr_stop_callwaiting(p);
+					chan_voicemngr_stop_callwaiting(sub_peer);
 
 					/* Cancel timer */
 					if (ast_sched_del(sched, sub_peer->cw_timer_id)) {
@@ -2841,7 +2871,7 @@ static void handle_Rkey_uk(struct chan_voicemngr_subchannel *sub, struct chan_vo
 			}
 			ast_log(LOG_NOTICE, " R in Call waiting\n");
 			/* Stop call waiting tone on current call */
-			chan_voicemngr_stop_callwaiting(p);
+			chan_voicemngr_stop_callwaiting(sub_peer);
 			/* Cancel timers */
 			if (ast_sched_del(sched, sub_peer->cw_timer_id)) {
 				ast_log(LOG_WARNING, "Failed to remove scheduled call waiting timer\n");
@@ -5371,20 +5401,6 @@ static int load_module(void)
 	return AST_MODULE_LOAD_FAILURE;
 }
 
-static int chan_voicemngr_signal_callwaiting(const struct chan_voicemngr_pvt *p)
-{
-	endpt_signal(p->line_id, "callwt", "on", NULL);
-	chan_voicemngr_send_ubus_event("CALLWAITING",p->line_id);
-	return 0;
-}
-
-static int chan_voicemngr_stop_callwaiting(const struct chan_voicemngr_pvt *p)
-{
-	endpt_signal(p->line_id, "callwt", "off", NULL);
-	chan_voicemngr_send_ubus_event("CALLWAITING_STOPPED", p->line_id);
-	return 0;
-}
-
 static int chan_voicemngr_signal_ringing(struct chan_voicemngr_pvt *p)
 {
 	if (channel_config[p->line_id].ringsignal) {
@@ -5494,13 +5510,14 @@ static int chan_voicemngr_signal_callerid(struct ast_channel *chan, struct chan_
 		str_length = strlen(clid_string.number_name);
 		clid_string.number_name[str_length++] = '"';
 		clid_string.number_name[str_length++] = '\0';
+		ast_debug(2, "CLIP info: caller number [%s], caller name [%s], final string [%s]\n",
+				number, name, (char *)&clid_string);
 
-		ast_debug(2, "CLIP info: caller number [%s], caller name [%s], final string [%s]\n", number, name, (char *)&clid_string);
-		if (callwt)
-			endpt_signal(sub->parent->line_id, "callwt", "on", (char *)&clid_string);
-		else
+		if (callwt) {
+			chan_voicemngr_signal_callwaiting(sub);
+		} else {
 			endpt_signal(sub->parent->line_id, "callid", "on", (char *)&clid_string);
-		return 0;
+		}
 	}
 
 	return 0;
-- 
GitLab