From 7f70d818cdefbf726d1519a264cc552ef72049df Mon Sep 17 00:00:00 2001
From: Yalu Zhang <yalu.zhang@iopsys.eu>
Date: Tue, 25 Jun 2024 08:17:56 +0000
Subject: [PATCH] Forward RTP telephone event packets to voicemngr

The frame type of RTP telephone event packets is AST_FRAME_DTMF_BYPASS.
---
 src/channels/chan_voicemngr.c | 144 +++++++++++++++++++---------------
 1 file changed, 79 insertions(+), 65 deletions(-)

diff --git a/src/channels/chan_voicemngr.c b/src/channels/chan_voicemngr.c
index 00d89b0..188067f 100644
--- a/src/channels/chan_voicemngr.c
+++ b/src/channels/chan_voicemngr.c
@@ -1283,7 +1283,7 @@ static int chan_voicemngr_senddigit_begin(struct ast_channel *ast, char digit)
 	struct chan_voicemngr_subchannel *sub;
 	char signal[10];
 
-	//ast_debug(5, "DTMF send begin: %c\n", digit);
+	ast_debug(5, "DTMF send begin: %c\n", digit);
 
 	sub = ast_channel_tech_pvt(ast);
 	pvt_lock(sub->parent, "DTMF senddigit_begin");
@@ -1301,7 +1301,7 @@ static int chan_voicemngr_senddigit_end(struct ast_channel *ast, char digit, uns
 	struct chan_voicemngr_subchannel *sub;
 	char signal[10];
 
-	//ast_debug(5, "DTMF send end: %c, %d ms\n", digit, duration);
+	ast_debug(5, "DTMF send end: %c, %d ms\n", digit, duration);
 
 	sub = ast_channel_tech_pvt(ast);
 	pvt_lock(sub->parent, "DTMF senddigit_end");
@@ -1754,7 +1754,7 @@ static int chan_voicemngr_classify_rtp_packet(int payload_type) {
 		if (voicemngr_codecs[i].rtp_payload_type == payload_type)
 			return CHAN_VOICEMNGR_AUDIO;
 
-	ast_verbose("Unknown rtp packet payload_type %d\n", payload_type);
+	ast_log(LOG_WARNING, "Unknown RTP payload_type %d\n", payload_type);
 	return CHAN_VOICEMNGR_UNKNOWN;
 }
 
@@ -1764,147 +1764,161 @@ static int map_ast_codec_id_to_rtp(const struct ast_format *astcodec)
 		if (ast_format_cmp(astcodec, *voicemngr_codecs[i].ast_format) == AST_FORMAT_CMP_EQUAL)
 			return voicemngr_codecs[i].rtp_payload_type;
 
-	ast_verbose("Unknown asterisk format/codec\n");
+	ast_log(LOG_WARNING, "Unknown asterisk format(%s), return PCMA\n", ast_format_get_name(astcodec));
 	return RTP_PT_PCMA;
 }
 
-
 static struct ast_frame  *chan_voicemngr_read(struct ast_channel *ast)
 {
 	return &ast_null_frame;
 }
 
-/* Handle stream events on audio_bus. Parses raw stream and calls 
-   registered packet handlers. */
+/* Handle stream events on audio_bus. Parses raw stream and calls registered packet handlers. */
 static void audio_rx_stream_handler(pe_stream_t *stream __attribute__((unused)), pe_event_t *event __attribute__((unused))) {
 	if (pe_bus_receive(audio_rx_bus, event) < 0) {
 		printf("audio_bus rx buffer full");
-		return;																	// Drop packets if we can't cope the pace
+		return; // Drop packets if we can't cope the pace
 	}
 
 	pe_bus_dispatch(audio_rx_bus);
 }
 
+/* Handle audio packets from Asterisk, i.e. from network */
 static int chan_voicemngr_write(struct ast_channel *ast, struct ast_frame *frame)
 {
 	struct chan_voicemngr_subchannel *sub = ast_channel_tech_pvt(ast);
-	int packet_size;
-	audio_packet_t *ap;
-	int sip_client_id = -1;
+	int packet_size, payload_type, sip_client_id = -1;
+	audio_packet_t *ap = NULL;
 
-	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 (sub->channel_state == ONHOLD || (ast_channel_state(ast) != AST_STATE_UP && ast_channel_state(ast) != AST_STATE_RING)) {
+		ast_debug(5, "Silently ignore the frame since the channel is not in the right state\n");
 		return 0;
 	}
 
-	/* Ignore if on hold */
-	if (sub->channel_state == ONHOLD) {
-		return 0;
-	}
-	if (frame->frametype == AST_FRAME_CNG) {
-		/*packet_size = sizeof(audio_packet_t) - 1 + RTP_HEADER_SIZE + frame->datalen;
-		CNG paket has only 1 data for the noise lvl, datalen=1*/
-		packet_size = sizeof(audio_packet_t) + RTP_HEADER_SIZE ;
+	if (frame->frametype == AST_FRAME_VOICE) {
+		packet_size = sizeof(audio_packet_t) - 1 + RTP_HEADER_SIZE + frame->datalen;
 		ap = ast_calloc(1, packet_size);
 		if (!ap) {
 			ast_log(LOG_ERROR, "Out of memory\n");
 			return -1;
 		}
-
 		ap->line = sub->parent->line_id;
 		ap->connection_id = sub->parent->line_id;
-		ap->rtp_size = RTP_HEADER_SIZE + 1;
+		ap->rtp_size = RTP_HEADER_SIZE + frame->datalen;
 
 		/* copy frame data to audio packet */
-		/* level = 127 - (level & 0x7f); */
-		ap->rtp[12]=127 - (frame->subclass.integer & 0x7f);
+		memcpy(ap->rtp + RTP_HEADER_SIZE, frame->data.ptr, frame->datalen);
 
-		//ast_mutex_lock(&sub->parent->lock);
 		pvt_lock(sub->parent, "TELCHAN write frame");
 
 		/* generate the rtp header */
-		chan_voicemngr_generate_rtp_packet(sub, ap->rtp, RTP_PT_CN, frame->seqno, frame->ts, frame->ssrc);
+		payload_type = map_ast_codec_id_to_rtp(frame->subclass.format);
+		chan_voicemngr_generate_rtp_packet(sub, ap->rtp, payload_type, frame->seqno, frame->ts, frame->ssrc);
 
 		sip_client_id = chan_voicemngr_get_sip_client_id(sub);
 		if (sip_client_id >= 0 && sip_client_id < MAX_SIP_CLIENTS) {
-				line_stats[sip_client_id].rxpkts++;
-				line_stats[sip_client_id].rxbytes += ap->rtp_size;
+			line_stats[sip_client_id].rxpkts++;
+			line_stats[sip_client_id].rxbytes += ap->rtp_size;
 		}
 
 		/* set rtp payload type sent to voicemngr */
-		sub->codec = RTP_PT_CN;
+		sub->codec = payload_type;
+		/* in case of media played during calling on an outgoing call and there has no codec set
+		 * and dsp do not have the cap to handle any payload type that received. */
+		if (sub->owner && ast_strlen_zero(ast_channel_codec_get(sub->owner))) {
+			ast_channel_codec_set(sub->owner, ast_format_get_name(frame->subclass.format));
+			ast_log(LOG_NOTICE, "set local codec to :%s \n", ast_channel_codec_get(sub->owner));
+		}
 
-		//ast_mutex_unlock(&sub->parent->lock);
 		pvt_unlock(sub->parent);
-		pe_bus_send(audio_tx_bus, (uint8_t *)ap, packet_size);
-		ast_free(ap);
-	}
-
-	if(frame->frametype == AST_FRAME_VOICE) {
-		packet_size = sizeof(audio_packet_t) - 1 + RTP_HEADER_SIZE + frame->datalen;
+		chan_voicemngr_modify_codec(sub); // in case of early media on outgoing
+	} else if(frame->frametype == AST_FRAME_RTCP) {
+		packet_size = sizeof(audio_packet_t) - 1 + frame->datalen;
 		ap = ast_calloc(1, packet_size);
 		if (!ap) {
 			ast_log(LOG_ERROR, "Out of memory\n");
 			return -1;
 		}
-
 		ap->line = sub->parent->line_id;
 		ap->connection_id = sub->parent->line_id;
-		ap->rtp_size = RTP_HEADER_SIZE + frame->datalen;
+		ap->rtp_size = frame->datalen;
 
 		/* copy frame data to audio packet */
-		memcpy(ap->rtp + RTP_HEADER_SIZE, frame->data.ptr, frame->datalen);
+		memcpy(ap->rtp, frame->data.ptr, frame->datalen);
+
+		chan_voicemngr_process_incoming_rtcp_packet(sub, ap->rtp, ap->rtp_size);
+	} else if (frame->frametype == AST_FRAME_CNG) {
+		/* CNG packet has only one byte for the noise level, i.e. frame->datalen=1*/
+		packet_size = sizeof(audio_packet_t) + RTP_HEADER_SIZE ;
+		ap = ast_calloc(1, packet_size);
+		if (!ap) {
+			ast_log(LOG_ERROR, "Out of memory\n");
+			return -1;
+		}
+		ap->line = sub->parent->line_id;
+		ap->connection_id = sub->parent->line_id;
+		ap->rtp_size = RTP_HEADER_SIZE + 1;
+
+		/* level = 127 - (level & 0x7f); */
+		ap->rtp[12]=127 - (frame->subclass.integer & 0x7f);
 
-		//ast_mutex_lock(&sub->parent->lock);
 		pvt_lock(sub->parent, "TELCHAN write frame");
 
 		/* generate the rtp header */
-		chan_voicemngr_generate_rtp_packet(sub, ap->rtp, map_ast_codec_id_to_rtp(frame->subclass.format), frame->seqno, frame->ts, frame->ssrc);
+		chan_voicemngr_generate_rtp_packet(sub, ap->rtp, RTP_PT_CN, frame->seqno, frame->ts, frame->ssrc);
 
 		sip_client_id = chan_voicemngr_get_sip_client_id(sub);
 		if (sip_client_id >= 0 && sip_client_id < MAX_SIP_CLIENTS) {
-				line_stats[sip_client_id].rxpkts++;
-				line_stats[sip_client_id].rxbytes += ap->rtp_size;
+			line_stats[sip_client_id].rxpkts++;
+			line_stats[sip_client_id].rxbytes += ap->rtp_size;
 		}
 
 		/* set rtp payload type sent to voicemngr */
-		sub->codec = map_ast_codec_id_to_rtp(frame->subclass.format);
-		//in case of media played during calling on an outgoing call and there has no codec set
-		//and dsp do not have the cap to handle any payload type that received.
-		if (sub->owner && ast_strlen_zero(ast_channel_codec_get(sub->owner))) {
-			ast_channel_codec_set(sub->owner, ast_format_get_name(frame->subclass.format));
-			ast_log(LOG_NOTICE, "set local codec to :%s \n", ast_channel_codec_get(sub->owner));
-		}
+		sub->codec = RTP_PT_CN;
 
 		//ast_mutex_unlock(&sub->parent->lock);
 		pvt_unlock(sub->parent);
-		chan_voicemngr_modify_codec(sub); //in case of early media on outgoing
-
-		pe_bus_send(audio_tx_bus, (uint8_t *)ap, packet_size);
-		ast_free(ap);
-	}
-
-	if(frame->frametype == AST_FRAME_RTCP) {
-		packet_size = sizeof(audio_packet_t) - 1 + frame->datalen;
+	} else if (frame->frametype == AST_FRAME_DTMF_BYPASS) {
+		ast_debug(0, "RTP for Telephone Event: payload type = %d, len = %d, event = %hhu, timestamp = %ld, seqno = %d\n",
+			frame->subclass.integer, frame->datalen, *(unsigned char *)frame->data.ptr, frame->ts, frame->seqno);
+		packet_size = sizeof(audio_packet_t) - 1 + RTP_HEADER_SIZE + frame->datalen;
 		ap = ast_calloc(1, packet_size);
 		if (!ap) {
 			ast_log(LOG_ERROR, "Out of memory\n");
 			return -1;
 		}
-
 		ap->line = sub->parent->line_id;
 		ap->connection_id = sub->parent->line_id;
-		ap->rtp_size = frame->datalen;
+		ap->rtp_size = RTP_HEADER_SIZE + frame->datalen;
 
 		/* copy frame data to audio packet */
-		memcpy(ap->rtp, frame->data.ptr, frame->datalen);
+		memcpy(ap->rtp + RTP_HEADER_SIZE, frame->data.ptr, frame->datalen);
 
-		chan_voicemngr_process_incoming_rtcp_packet(sub, ap->rtp, ap->rtp_size);
+		pvt_lock(sub->parent, "TELCHAN write frame");
 
+		/* generate the rtp header */
+		payload_type = frame->subclass.integer;
+		chan_voicemngr_generate_rtp_packet(sub, ap->rtp, payload_type, frame->seqno, frame->ts, frame->ssrc);
+
+		sip_client_id = chan_voicemngr_get_sip_client_id(sub);
+		if (sip_client_id >= 0 && sip_client_id < MAX_SIP_CLIENTS) {
+			line_stats[sip_client_id].rxpkts++;
+			line_stats[sip_client_id].rxbytes += ap->rtp_size;
+		}
+
+		pvt_unlock(sub->parent);
+	} else {
+		char ftype[40] = "Unknown frame type";
+
+		ast_frame_type2str(frame->frametype, ftype, sizeof(ftype));
+		ast_log(LOG_WARNING, "Unsupported frame type, %s(%d)\n", ftype, frame->frametype);
+	}
+
+	if (ap) {
 		pe_bus_send(audio_tx_bus, (uint8_t *)ap, packet_size);
 		ast_free(ap);
 	}
+
 	return 0;
 }
 
@@ -3294,7 +3308,7 @@ static void audio_packet_handler(pe_packet_t *p) {
 		} else if(payload_type == sub->dtmf_pt) {
 			frame.frametype = AST_FRAME_DTMF_BYPASS;
 			frame.subclass.integer = ap->rtp[1]; //payload_type
-			ast_debug(3, "DTMF rtp_event for digit: %d, with payload_type: %d \n", ap->rtp[12], ap->rtp[1]);
+			ast_debug(3, "DTMF rtp_event for digit: %d, with payload_type: %d \n", ap->rtp[12], payload_type);
 			if (sub->conference_initiator == 1) {
 				drop_frame=1;
 			}
-- 
GitLab