voice_engine/media/codecs/
mod.rs1use crate::media::{PcmBuf, Sample};
2pub mod g722;
3#[cfg(feature = "g729")]
4pub mod g729;
5#[cfg(feature = "opus")]
6pub mod opus;
7pub mod pcma;
8pub mod pcmu;
9pub mod resample;
10pub mod telephone_event;
11#[cfg(test)]
12mod tests;
13#[derive(Debug, Clone, Copy, Eq, Ord, PartialEq, PartialOrd)]
14pub enum CodecType {
15 PCMU,
16 PCMA,
17 G722,
18 #[cfg(feature = "g729")]
19 G729,
20 #[cfg(feature = "opus")]
21 Opus,
22 TelephoneEvent,
23}
24
25pub trait Decoder: Send + Sync {
26 fn decode(&mut self, data: &[u8]) -> PcmBuf;
28
29 fn sample_rate(&self) -> u32;
31
32 fn channels(&self) -> u16;
34}
35
36pub trait Encoder: Send + Sync {
37 fn encode(&mut self, samples: &[Sample]) -> Vec<u8>;
39
40 fn sample_rate(&self) -> u32;
42
43 fn channels(&self) -> u16;
45}
46
47pub fn create_decoder(codec: CodecType) -> Box<dyn Decoder> {
48 match codec {
49 CodecType::PCMU => Box::new(pcmu::PcmuDecoder::new()),
50 CodecType::PCMA => Box::new(pcma::PcmaDecoder::new()),
51 CodecType::G722 => Box::new(g722::G722Decoder::new()),
52 #[cfg(feature = "g729")]
53 CodecType::G729 => Box::new(g729::G729Decoder::new()),
54 #[cfg(feature = "opus")]
55 CodecType::Opus => Box::new(opus::OpusDecoder::new_default()),
56 CodecType::TelephoneEvent => Box::new(telephone_event::TelephoneEventDecoder::new()),
57 }
58}
59
60pub fn create_encoder(codec: CodecType) -> Box<dyn Encoder> {
61 match codec {
62 CodecType::PCMU => Box::new(pcmu::PcmuEncoder::new()),
63 CodecType::PCMA => Box::new(pcma::PcmaEncoder::new()),
64 CodecType::G722 => Box::new(g722::G722Encoder::new()),
65 #[cfg(feature = "g729")]
66 CodecType::G729 => Box::new(g729::G729Encoder::new()),
67 #[cfg(feature = "opus")]
68 CodecType::Opus => Box::new(opus::OpusEncoder::new_default()),
69 CodecType::TelephoneEvent => Box::new(telephone_event::TelephoneEventEncoder::new()),
70 }
71}
72
73impl CodecType {
74 pub fn mime_type(&self) -> &str {
75 match self {
76 CodecType::PCMU => "audio/PCMU",
77 CodecType::PCMA => "audio/PCMA",
78 CodecType::G722 => "audio/G722",
79 #[cfg(feature = "g729")]
80 CodecType::G729 => "audio/G729",
81 #[cfg(feature = "opus")]
82 CodecType::Opus => "audio/opus",
83 CodecType::TelephoneEvent => "audio/telephone-event",
84 }
85 }
86 pub fn rtpmap(&self) -> &str {
87 match self {
88 CodecType::PCMU => "PCMU/8000",
89 CodecType::PCMA => "PCMA/8000",
90 CodecType::G722 => "G722/8000",
91 #[cfg(feature = "g729")]
92 CodecType::G729 => "G729/8000",
93 #[cfg(feature = "opus")]
94 CodecType::Opus => "opus/48000/2",
95 CodecType::TelephoneEvent => "telephone-event/8000",
96 }
97 }
98 pub fn fmtp(&self) -> Option<&str> {
99 match self {
100 CodecType::PCMU => None,
101 CodecType::PCMA => None,
102 CodecType::G722 => None,
103 #[cfg(feature = "g729")]
104 CodecType::G729 => None,
105 #[cfg(feature = "opus")]
106 CodecType::Opus => Some("minptime=10;useinbandfec=1"),
107 CodecType::TelephoneEvent => Some("0-16"),
108 }
109 }
110
111 pub fn clock_rate(&self) -> u32 {
112 match self {
113 CodecType::PCMU => 8000,
114 CodecType::PCMA => 8000,
115 CodecType::G722 => 8000,
116 #[cfg(feature = "g729")]
117 CodecType::G729 => 8000,
118 #[cfg(feature = "opus")]
119 CodecType::Opus => 48000,
120 CodecType::TelephoneEvent => 8000,
121 }
122 }
123
124 pub fn channels(&self) -> u16 {
125 match self {
126 #[cfg(feature = "opus")]
127 CodecType::Opus => 2,
128 _ => 1,
129 }
130 }
131
132 pub fn payload_type(&self) -> u8 {
133 match self {
134 CodecType::PCMU => 0,
135 CodecType::PCMA => 8,
136 CodecType::G722 => 9,
137 #[cfg(feature = "g729")]
138 CodecType::G729 => 18,
139 #[cfg(feature = "opus")]
140 CodecType::Opus => 111,
141 CodecType::TelephoneEvent => 101,
142 }
143 }
144 pub fn samplerate(&self) -> u32 {
145 match self {
146 CodecType::PCMU => 8000,
147 CodecType::PCMA => 8000,
148 CodecType::G722 => 16000,
149 #[cfg(feature = "g729")]
150 CodecType::G729 => 8000,
151 #[cfg(feature = "opus")]
152 CodecType::Opus => 48000,
153 CodecType::TelephoneEvent => 8000,
154 }
155 }
156 pub fn is_audio(&self) -> bool {
157 match self {
158 CodecType::PCMU | CodecType::PCMA | CodecType::G722 => true,
159 #[cfg(feature = "g729")]
160 CodecType::G729 => true,
161 #[cfg(feature = "opus")]
162 CodecType::Opus => true,
163 _ => false,
164 }
165 }
166
167 pub fn is_dynamic(&self) -> bool {
168 match self {
169 #[cfg(feature = "opus")]
170 CodecType::Opus => true,
171 CodecType::TelephoneEvent => true,
172 _ => false,
173 }
174 }
175}
176
177impl TryFrom<u8> for CodecType {
178 type Error = anyhow::Error;
179
180 fn try_from(value: u8) -> Result<Self, Self::Error> {
181 match value {
182 0 => Ok(CodecType::PCMU),
183 8 => Ok(CodecType::PCMA),
184 9 => Ok(CodecType::G722),
185 #[cfg(feature = "g729")]
186 18 => Ok(CodecType::G729), #[cfg(feature = "opus")]
188 101 => Ok(CodecType::TelephoneEvent),
190 111 => Ok(CodecType::Opus), _ => Err(anyhow::anyhow!("Invalid codec type: {}", value)),
192 }
193 }
194}
195
196pub fn parse_rtpmap(rtpmap: &str) -> Result<(u8, CodecType, u32, u16), anyhow::Error> {
214 if let [payload_type_str, codec_spec] = rtpmap.split(' ').collect::<Vec<&str>>().as_slice() {
215 let payload_type = payload_type_str
217 .parse::<u8>()
218 .map_err(|e| anyhow::anyhow!("Failed to parse payload type: {}", e))?;
219 let codec_parts: Vec<&str> = codec_spec.split('/').collect();
220
221 if let [codec_name, clock_rate_str, channel_count @ ..] = codec_parts.as_slice() {
222 let codec_type = match codec_name.to_lowercase().as_str() {
223 "pcmu" => CodecType::PCMU,
224 "pcma" => CodecType::PCMA,
225 "g722" => CodecType::G722,
226 #[cfg(feature = "g729")]
227 "g729" => CodecType::G729,
228 #[cfg(feature = "opus")]
229 "opus" => CodecType::Opus,
230 "telephone-event" => CodecType::TelephoneEvent,
231 _ => return Err(anyhow::anyhow!("Unsupported codec name: {}", codec_name)),
232 };
233
234 let clock_rate = clock_rate_str
235 .parse::<u32>()
236 .map_err(|e| anyhow::anyhow!("Failed to parse clock rate: {}", e))?;
237
238 let channel_count = match channel_count {
239 ["2"] => 2,
240 _ => 1,
241 };
242 Ok((payload_type, codec_type, clock_rate, channel_count))
243 } else {
244 return Err(anyhow::anyhow!("Invalid codec specification in rtpmap"));
245 }
246 } else {
247 Err(anyhow::anyhow!(
248 "Invalid rtpmap format: missing space between payload type and encoding name"
249 ))
250 }
251}
252
253#[cfg(target_endian = "little")]
254pub fn samples_to_bytes(samples: &[Sample]) -> Vec<u8> {
255 unsafe {
256 std::slice::from_raw_parts(
257 samples.as_ptr() as *const u8,
258 samples.len() * std::mem::size_of::<Sample>(),
259 )
260 .to_vec()
261 }
262}
263
264#[cfg(target_endian = "big")]
265pub fn samples_to_bytes(samples: &[Sample]) -> Vec<u8> {
266 samples.iter().flat_map(|s| s.to_le_bytes()).collect()
267}
268
269#[cfg(target_endian = "little")]
270pub fn bytes_to_samples(u8_data: &[u8]) -> PcmBuf {
271 unsafe {
272 std::slice::from_raw_parts(
273 u8_data.as_ptr() as *const Sample,
274 u8_data.len() / std::mem::size_of::<Sample>(),
275 )
276 .to_vec()
277 }
278}
279#[cfg(target_endian = "big")]
280pub fn bytes_to_samples(u8_data: &[u8]) -> PcmBuf {
281 u8_data
282 .chunks(2)
283 .map(|chunk| (chunk[0] as i16) | ((chunk[1] as i16) << 8))
284 .collect()
285}