From 2d1933633a4780dbf1572f6e7653ee57979131c9 Mon Sep 17 00:00:00 2001
From: Richard Fuchs <rfuchs@sipwise.com>
Date: Tue, 15 Apr 2025 14:14:23 -0400
Subject: [PATCH] MT#55283 remember DTMF start TS

In case stray audio is receveived before the DTMF event is fully
finished, retain the internal state from the beginning of the event to
make sure that the output is consistent. This fixes both DTMF timestamps
jumping during the end event, and audio timestamps being off after the
end event.

Closes #1929

Change-Id: I4e675b15153e35a8d1546b947e4b82879c1577b4
---
 daemon/codec.c | 40 ++++++++++++++++++++++++----------------
 1 file changed, 24 insertions(+), 16 deletions(-)

diff --git a/daemon/codec.c b/daemon/codec.c
index f94cf8c42..c03e34e6f 100644
--- a/daemon/codec.c
+++ b/daemon/codec.c
@@ -220,7 +220,7 @@ struct codec_ssrc_handler {
 	dtmf_rx_state_t *dtmf_dsp;
 	resample_t dtmf_resampler;
 	format_t dtmf_format;
-	uint64_t dtmf_ts, last_dtmf_event_ts;
+	uint64_t dtmf_ts, last_dtmf_event_ts, dtmf_out_ts;
 	dtmf_event_q dtmf_events;
 	struct dtmf_event dtmf_event; // for replacing PCM with DTMF event
 	struct dtmf_event dtmf_state; // state tracker for DTMF actions
@@ -2515,19 +2515,21 @@ static int codec_add_dtmf_packet(struct codec_ssrc_handler *ch, struct codec_ssr
 		// this is a new event
 		ch->dtmf_ts = packet->ts; // start TS
 		ch->last_dtmf_event_ts = 0; // last DTMF event duration
-	}
 
-	unsigned long ts = fraction_divl(output_ch->encoder->next_pts, &output_ch->encoder->clockrate_fact);
-	// roll back TS to start of event
-	ts -= ch->last_dtmf_event_ts;
-	// adjust to output RTP TS
-	unsigned long packet_ts = ts + output_ch->csch.first_ts;
+		unsigned long ts = fraction_divl(output_ch->encoder->next_pts, &output_ch->encoder->clockrate_fact);
+		// roll back TS to start of event
+		ts -= ch->last_dtmf_event_ts;
+		// adjust to output RTP TS
+		unsigned long packet_ts = ts + output_ch->csch.first_ts;
+		ch->dtmf_out_ts = packet_ts;
 
-	ilogs(transcoding, LOG_DEBUG, "Scaling DTMF packet timestamp and duration: TS %lu -> %lu "
-			"(%u -> %u)",
-			packet->ts, packet_ts,
-			h->source_pt.clock_rate, h->dest_pt.clock_rate);
-	packet->ts = packet_ts;
+		ilogs(transcoding, LOG_DEBUG, "Scaling DTMF packet timestamp and duration: TS %lu -> %lu "
+				"(%u -> %u)",
+				packet->ts, packet_ts,
+				h->source_pt.clock_rate, h->dest_pt.clock_rate);
+	}
+
+	packet->ts = ch->dtmf_out_ts;
 
 	if (packet->payload->len >= sizeof(struct telephone_event_payload)) {
 		struct telephone_event_payload *dtmf = (void *) packet->payload->s;
@@ -2541,10 +2543,16 @@ static int codec_add_dtmf_packet(struct codec_ssrc_handler *ch, struct codec_ssr
 			ch->dtmf_first_duration = duration;
 		ts_delay = duration - ch->dtmf_first_duration;
 
-		// shift forward our output RTP TS
-		output_ch->encoder->next_pts = fraction_multl(ts + duration, &output_ch->encoder->clockrate_fact);
-		output_ch->encoder->packet_pts += fraction_multl(duration - ch->last_dtmf_event_ts, &output_ch->encoder->clockrate_fact);
-		ch->last_dtmf_event_ts = duration;
+		if (ch->last_dtmf_event_ts != duration) {
+			// shift forward our output RTP TS
+			output_ch->encoder->next_pts
+				= fraction_multl(packet->ts - output_ch->csch.first_ts + duration,
+					&output_ch->encoder->clockrate_fact);
+			output_ch->encoder->packet_pts
+				+= fraction_multl(duration - ch->last_dtmf_event_ts,
+					&output_ch->encoder->clockrate_fact);
+			ch->last_dtmf_event_ts = duration;
+		}
 	}
 	payload_type = h->dtmf_payload_type;
 	if (payload_type == -1)