1use std::sync::atomic::{AtomicU8, AtomicU16};
2const DTMF_EVENT_0: u8 = 0;
4const DTMF_EVENT_1: u8 = 1;
5const DTMF_EVENT_2: u8 = 2;
6const DTMF_EVENT_3: u8 = 3;
7const DTMF_EVENT_4: u8 = 4;
8const DTMF_EVENT_5: u8 = 5;
9const DTMF_EVENT_6: u8 = 6;
10const DTMF_EVENT_7: u8 = 7;
11const DTMF_EVENT_8: u8 = 8;
12const DTMF_EVENT_9: u8 = 9;
13const DTMF_EVENT_STAR: u8 = 10;
14const DTMF_EVENT_POUND: u8 = 11;
15const DTMF_EVENT_A: u8 = 12;
16const DTMF_EVENT_B: u8 = 13;
17const DTMF_EVENT_C: u8 = 14;
18const DTMF_EVENT_D: u8 = 15;
19
20pub struct DtmfDetector {
21 last_event: AtomicU8,
23 last_duration: AtomicU16,
24 pub refer: Option<bool>,
25 pub suppress_dtmf_forward: bool,
26}
27
28#[derive(Debug)]
29struct DtmfPayload {
30 event: u8, #[allow(dead_code)]
32 is_end: bool, _reserved: u8, _volume: u8, duration: u16, }
37
38impl DtmfPayload {
39 fn parse(payload: &[u8]) -> Option<Self> {
40 if payload.len() < 4 {
41 return None;
42 }
43
44 let event = payload[0];
45 if event > DTMF_EVENT_D {
46 return None;
47 }
48
49 let is_end = (payload[1] & 0b1000_0000) != 0;
58 let reserved = payload[1] & 0b0100_0000;
60 let volume = payload[1] & 0b0011_1111;
62
63 let duration_high = payload[2] as u16;
65 let duration_low = payload[3] as u16;
66 let duration = (duration_high << 8) | duration_low;
67
68 Some(Self {
69 event,
70 is_end,
71 _reserved: reserved,
72 _volume: volume,
73 duration,
74 })
75 }
76}
77
78impl DtmfDetector {
79 pub fn new() -> Self {
80 Self {
81 last_event: AtomicU8::new(0xFF),
82 last_duration: AtomicU16::new(0),
83 refer: None,
84 suppress_dtmf_forward: false,
85 }
86 }
87
88 pub fn detect_rtp(&self, payload_type: u8, payload: &[u8]) -> Option<String> {
90 if payload.len() < 4 {
93 return None;
94 }
95
96 if payload_type < 96 || payload_type > 127 {
98 return None;
99 }
100
101 let dtmf_payload = DtmfPayload::parse(payload)?;
103
104 let current_event = dtmf_payload.event;
106 let current_duration = dtmf_payload.duration;
107 let last_event = self
108 .last_event
109 .swap(current_event, std::sync::atomic::Ordering::Relaxed);
110 let last_duration = self
111 .last_duration
112 .swap(current_duration, std::sync::atomic::Ordering::Relaxed);
113
114 if current_event == last_event && current_duration >= last_duration {
115 return None;
116 }
117
118 Some(
119 match dtmf_payload.event {
120 DTMF_EVENT_0 => "0",
121 DTMF_EVENT_1 => "1",
122 DTMF_EVENT_2 => "2",
123 DTMF_EVENT_3 => "3",
124 DTMF_EVENT_4 => "4",
125 DTMF_EVENT_5 => "5",
126 DTMF_EVENT_6 => "6",
127 DTMF_EVENT_7 => "7",
128 DTMF_EVENT_8 => "8",
129 DTMF_EVENT_9 => "9",
130 DTMF_EVENT_STAR => "*",
131 DTMF_EVENT_POUND => "#",
132 DTMF_EVENT_A => "A",
133 DTMF_EVENT_B => "B",
134 DTMF_EVENT_C => "C",
135 DTMF_EVENT_D => "D",
136 _ => return None, }
138 .to_string(),
139 )
140 }
141}
142
143#[cfg(test)]
144mod tests {
145 use super::*;
146
147 #[test]
148 fn test_dtmf_payload_parse() {
149 let payload = [1, 0x8A, 0, 160]; let dtmf = DtmfPayload::parse(&payload).unwrap();
154 assert_eq!(dtmf.event, 1);
155 assert_eq!(dtmf.is_end, true);
156 assert_eq!(dtmf._reserved, 0);
157 assert_eq!(dtmf._volume, 10); assert_eq!(dtmf.duration, 160);
159
160 let payload = [2, 0x00, 0, 160]; let dtmf = DtmfPayload::parse(&payload).unwrap();
164 assert_eq!(dtmf.event, 2);
165 assert_eq!(dtmf.is_end, false);
166 assert_eq!(dtmf._volume, 0);
167
168 let payload = [20, 0x80, 10, 100]; assert!(DtmfPayload::parse(&payload).is_none());
171
172 let payload = [1, 0x80, 10]; assert!(DtmfPayload::parse(&payload).is_none());
175
176 let payload = [2, 138, 3, 32];
178 let dtmf = DtmfPayload::parse(&payload).unwrap();
184 assert_eq!(dtmf.event, 2); assert_eq!(dtmf.is_end, true); assert_eq!(dtmf._reserved, 0); assert_eq!(dtmf._volume, 10); assert_eq!(dtmf.duration, 800); }
190
191 #[test]
192 fn test_dtmf_detection() {
193 let detector = DtmfDetector::new();
194
195 {
197 let payload = [DTMF_EVENT_5, 0x80, 10, 100];
199
200 let digit = detector.detect_rtp(101, &payload);
202 assert_eq!(digit, Some("5".to_string()));
203
204 let digit = detector.detect_rtp(0, &payload);
206 assert_eq!(digit, None);
207
208 let payload = [DTMF_EVENT_5, 0x00, 10, 100];
210 let digit = detector.detect_rtp(101, &payload);
211 assert_eq!(digit, None);
212 }
213
214 {
216 let detector = DtmfDetector::new(); let payload1 = [DTMF_EVENT_5, 0x80, 0, 100]; let digit1 = detector.detect_rtp(101, &payload1);
221 assert_eq!(digit1, Some("5".to_string()));
222
223 let payload2 = [DTMF_EVENT_5, 0x80, 0, 100]; let digit2 = detector.detect_rtp(101, &payload2);
226 assert_eq!(digit2, None);
227
228 let payload4 = [DTMF_EVENT_6, 0x80, 1, 8]; let digit4 = detector.detect_rtp(101, &payload4);
231 assert_eq!(digit4, Some("6".to_string()));
232 }
233 }
234
235 #[test]
236 fn test_dtmf_digit_0_first_press() {
237 let detector = DtmfDetector::new();
238
239 let payload = [DTMF_EVENT_0, 0x80, 0, 160]; let digit = detector.detect_rtp(101, &payload);
241 assert_eq!(
242 digit,
243 Some("0".to_string()),
244 "First press of digit '0' should be recognized"
245 );
246
247 let payload2 = [DTMF_EVENT_0, 0x80, 0, 160]; let digit2 = detector.detect_rtp(101, &payload2);
250 assert_eq!(digit2, None, "Duplicate DTMF should be filtered out");
251
252 let payload3 = [DTMF_EVENT_0, 0x80, 0, 80]; let digit3 = detector.detect_rtp(101, &payload3);
255 assert_eq!(
256 digit3,
257 Some("0".to_string()),
258 "Second press with smaller duration should be recognized as new press"
259 );
260 }
261}