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}
25
26#[derive(Debug)]
27struct DtmfPayload {
28 event: u8, #[allow(dead_code)]
30 is_end: bool, _reserved: u8, _volume: u8, duration: u16, }
35
36impl DtmfPayload {
37 fn parse(payload: &[u8]) -> Option<Self> {
38 if payload.len() < 4 {
39 return None;
40 }
41
42 let event = payload[0];
43 if event > DTMF_EVENT_D {
44 return None;
45 }
46
47 let is_end = (payload[1] & 0b1000_0000) != 0;
56 let reserved = payload[1] & 0b0100_0000;
58 let volume = payload[1] & 0b0011_1111;
60
61 let duration_high = payload[2] as u16;
63 let duration_low = payload[3] as u16;
64 let duration = (duration_high << 8) | duration_low;
65
66 Some(Self {
67 event,
68 is_end,
69 _reserved: reserved,
70 _volume: volume,
71 duration,
72 })
73 }
74}
75
76impl DtmfDetector {
77 pub fn new() -> Self {
78 Self {
79 last_event: AtomicU8::new(0),
80 last_duration: AtomicU16::new(0),
81 }
82 }
83
84 pub fn detect_rtp(&self, payload_type: u8, payload: &[u8]) -> Option<String> {
86 if payload.len() < 4 {
89 return None;
90 }
91
92 if payload_type < 96 || payload_type > 127 {
94 return None;
95 }
96
97 let dtmf_payload = DtmfPayload::parse(payload)?;
99
100 let current_event = dtmf_payload.event;
102 let current_duration = dtmf_payload.duration;
103 let last_event = self
104 .last_event
105 .swap(current_event, std::sync::atomic::Ordering::Relaxed);
106 let last_duration = self
107 .last_duration
108 .swap(current_duration, std::sync::atomic::Ordering::Relaxed);
109
110 if current_event == last_event && current_duration >= last_duration {
111 return None;
112 }
113
114 Some(
115 match dtmf_payload.event {
116 DTMF_EVENT_0 => "0",
117 DTMF_EVENT_1 => "1",
118 DTMF_EVENT_2 => "2",
119 DTMF_EVENT_3 => "3",
120 DTMF_EVENT_4 => "4",
121 DTMF_EVENT_5 => "5",
122 DTMF_EVENT_6 => "6",
123 DTMF_EVENT_7 => "7",
124 DTMF_EVENT_8 => "8",
125 DTMF_EVENT_9 => "9",
126 DTMF_EVENT_STAR => "*",
127 DTMF_EVENT_POUND => "#",
128 DTMF_EVENT_A => "A",
129 DTMF_EVENT_B => "B",
130 DTMF_EVENT_C => "C",
131 DTMF_EVENT_D => "D",
132 _ => return None, }
134 .to_string(),
135 )
136 }
137}
138
139#[cfg(test)]
140mod tests {
141 use super::*;
142
143 #[test]
144 fn test_dtmf_payload_parse() {
145 let payload = [1, 0x8A, 0, 160]; let dtmf = DtmfPayload::parse(&payload).unwrap();
150 assert_eq!(dtmf.event, 1);
151 assert_eq!(dtmf.is_end, true);
152 assert_eq!(dtmf._reserved, 0);
153 assert_eq!(dtmf._volume, 10); assert_eq!(dtmf.duration, 160);
155
156 let payload = [2, 0x00, 0, 160]; let dtmf = DtmfPayload::parse(&payload).unwrap();
160 assert_eq!(dtmf.event, 2);
161 assert_eq!(dtmf.is_end, false);
162 assert_eq!(dtmf._volume, 0);
163
164 let payload = [20, 0x80, 10, 100]; assert!(DtmfPayload::parse(&payload).is_none());
167
168 let payload = [1, 0x80, 10]; assert!(DtmfPayload::parse(&payload).is_none());
171
172 let payload = [2, 138, 3, 32];
174 let dtmf = DtmfPayload::parse(&payload).unwrap();
180 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); }
186
187 #[test]
188 fn test_dtmf_detection() {
189 let detector = DtmfDetector::new();
190
191 {
193 let payload = [DTMF_EVENT_5, 0x80, 10, 100];
195
196 let digit = detector.detect_rtp(101, &payload);
198 assert_eq!(digit, Some("5".to_string()));
199
200 let digit = detector.detect_rtp(0, &payload);
202 assert_eq!(digit, None);
203
204 let payload = [DTMF_EVENT_5, 0x00, 10, 100];
206 let digit = detector.detect_rtp(101, &payload);
207 assert_eq!(digit, None);
208 }
209
210 {
212 let detector = DtmfDetector::new(); let payload1 = [DTMF_EVENT_5, 0x80, 0, 100]; let digit1 = detector.detect_rtp(101, &payload1);
217 assert_eq!(digit1, Some("5".to_string()));
218
219 let payload2 = [DTMF_EVENT_5, 0x80, 0, 100]; let digit2 = detector.detect_rtp(101, &payload2);
222 assert_eq!(digit2, None);
223
224 let payload4 = [DTMF_EVENT_6, 0x80, 1, 8]; let digit4 = detector.detect_rtp(101, &payload4);
227 assert_eq!(digit4, Some("6".to_string()));
228 }
229 }
230}