Skip to main content

active_call/media/track/
track_codec.rs

1use crate::{media::AudioFrame, media::PcmBuf, media::Samples};
2use audio_codec::{
3    CodecType, Decoder, Encoder, Resampler, bytes_to_samples,
4    g722::{G722Decoder, G722Encoder},
5    pcma::{PcmaDecoder, PcmaEncoder},
6    pcmu::{PcmuDecoder, PcmuEncoder},
7    samples_to_bytes,
8};
9use std::collections::HashMap;
10use std::sync::{Arc, RwLock};
11
12use audio_codec::g729::{G729Decoder, G729Encoder};
13#[cfg(feature = "opus")]
14use audio_codec::opus::{OpusDecoder, OpusEncoder};
15
16pub struct TrackCodec {
17    pcmu_encoder: PcmuEncoder,
18    pcmu_decoder: PcmuDecoder,
19    pcma_encoder: PcmaEncoder,
20    pcma_decoder: PcmaDecoder,
21
22    g722_encoder: Option<Box<G722Encoder>>,
23    g722_decoder: Option<Box<G722Decoder>>,
24
25    g729_encoder: Option<Box<G729Encoder>>,
26    g729_decoder: Option<Box<G729Decoder>>,
27
28    #[cfg(feature = "opus")]
29    opus_encoder: Option<OpusEncoder>,
30    #[cfg(feature = "opus")]
31    opus_decoder: Option<OpusDecoder>,
32
33    resampler: Option<Resampler>,
34    resampler_in_rate: u32,
35    resampler_out_rate: u32,
36    pub payload_type_map: Arc<RwLock<HashMap<u8, CodecType>>>,
37}
38
39impl Clone for TrackCodec {
40    fn clone(&self) -> Self {
41        let mut new = Self::new();
42        // Share the same underlying map so reinvite PT updates are visible to all clones.
43        new.payload_type_map = Arc::clone(&self.payload_type_map);
44        new
45    }
46}
47
48impl TrackCodec {
49    pub fn new() -> Self {
50        let mut map = HashMap::new();
51        map.insert(0, CodecType::PCMU);
52        map.insert(8, CodecType::PCMA);
53        map.insert(9, CodecType::G722);
54        map.insert(18, CodecType::G729);
55        map.insert(101, CodecType::TelephoneEvent);
56        #[cfg(feature = "opus")]
57        map.insert(111, CodecType::Opus);
58        let payload_type_map = Arc::new(RwLock::new(map));
59
60        Self {
61            pcmu_encoder: PcmuEncoder::new(),
62            pcmu_decoder: PcmuDecoder::new(),
63            pcma_encoder: PcmaEncoder::new(),
64            pcma_decoder: PcmaDecoder::new(),
65            g722_encoder: None,
66            g722_decoder: None,
67            g729_encoder: None,
68            g729_decoder: None,
69            #[cfg(feature = "opus")]
70            opus_encoder: None,
71            #[cfg(feature = "opus")]
72            opus_decoder: None,
73            resampler: None,
74            resampler_in_rate: 0,
75            resampler_out_rate: 0,
76            payload_type_map,
77        }
78    }
79
80    pub fn set_payload_type(&mut self, pt: u8, codec: CodecType) {
81        self.payload_type_map.write().unwrap().insert(pt, codec);
82    }
83
84    /// Look up the codec for a given RTP payload type, consulting the negotiated map first
85    /// and falling back to the static payload type registry.
86    pub fn get_codec_for_pt(&self, pt: u8) -> Option<CodecType> {
87        self.payload_type_map
88            .read()
89            .unwrap()
90            .get(&pt)
91            .cloned()
92            .or_else(|| CodecType::try_from(pt).ok())
93    }
94
95    pub fn is_audio(payload_type: u8) -> bool {
96        match payload_type {
97            0 | 8 | 9 | 18 | 111 => true,
98            101 => false, // Telephone Event (DTMF) should not be decoded as audio
99            pt if pt >= 96 && pt <= 127 => true,
100            _ => false,
101        }
102    }
103
104    pub fn decode(
105        &mut self,
106        payload_type: u8,
107        payload: &[u8],
108        target_sample_rate: u32,
109    ) -> (u32, u16, PcmBuf) {
110        let codec = self
111            .payload_type_map
112            .read()
113            .unwrap()
114            .get(&payload_type)
115            .cloned()
116            .unwrap_or_else(|| match payload_type {
117                0 => CodecType::PCMU,
118                8 => CodecType::PCMA,
119                9 => CodecType::G722,
120                18 => CodecType::G729,
121                #[cfg(feature = "opus")]
122                111 => CodecType::Opus,
123                _ => CodecType::PCMU,
124            });
125
126        let pcm = match codec {
127            CodecType::PCMU => self.pcmu_decoder.decode(payload),
128            CodecType::PCMA => self.pcma_decoder.decode(payload),
129            CodecType::G722 => self
130                .g722_decoder
131                .get_or_insert_with(|| Box::new(G722Decoder::new()))
132                .decode(payload),
133            CodecType::G729 => self
134                .g729_decoder
135                .get_or_insert_with(|| Box::new(G729Decoder::new()))
136                .decode(payload),
137            #[cfg(feature = "opus")]
138            CodecType::Opus => self
139                .opus_decoder
140                .get_or_insert_with(OpusDecoder::new_default)
141                .decode(payload),
142            _ => bytes_to_samples(payload),
143        };
144
145        let (in_rate, channels) = match codec {
146            CodecType::PCMU => (8000, 1),
147            CodecType::PCMA => (8000, 1),
148            CodecType::G722 => (16000, 1),
149            CodecType::G729 => (8000, 1),
150            #[cfg(feature = "opus")]
151            CodecType::Opus => {
152                if pcm.len() >= 1920 {
153                    (48000, 2)
154                } else {
155                    (48000, 1)
156                }
157            }
158            _ => (8000, 1),
159        };
160
161        (
162            target_sample_rate,
163            channels,
164            self.resample(pcm, in_rate, target_sample_rate),
165        )
166    }
167
168    pub fn resample(&mut self, pcm: PcmBuf, in_rate: u32, out_rate: u32) -> PcmBuf {
169        if in_rate == out_rate {
170            return pcm;
171        }
172
173        if self.resampler.is_none()
174            || self.resampler_in_rate != in_rate
175            || self.resampler_out_rate != out_rate
176        {
177            self.resampler = Some(Resampler::new(in_rate as usize, out_rate as usize));
178            self.resampler_in_rate = in_rate;
179            self.resampler_out_rate = out_rate;
180        }
181        self.resampler.as_mut().unwrap().resample(&pcm)
182    }
183
184    pub fn encode(&mut self, payload_type: u8, frame: AudioFrame) -> (u8, Vec<u8>) {
185        match frame.samples {
186            Samples::PCM { samples: mut pcm } => {
187                let codec = self
188                    .payload_type_map
189                    .read()
190                    .unwrap()
191                    .get(&payload_type)
192                    .cloned()
193                    .or_else(|| CodecType::try_from(payload_type).ok());
194
195                let target_samplerate = codec.map(|c| c.samplerate()).unwrap_or(8000);
196                if frame.sample_rate != target_samplerate {
197                    if self.resampler.is_none()
198                        || self.resampler_in_rate != frame.sample_rate
199                        || self.resampler_out_rate != target_samplerate
200                    {
201                        self.resampler = Some(Resampler::new(
202                            frame.sample_rate as usize,
203                            target_samplerate as usize,
204                        ));
205                        self.resampler_in_rate = frame.sample_rate;
206                        self.resampler_out_rate = target_samplerate;
207                    }
208                    pcm = self.resampler.as_mut().unwrap().resample(&pcm);
209                }
210
211                let payload = match codec {
212                    Some(CodecType::PCMU) => self.pcmu_encoder.encode(&pcm),
213                    Some(CodecType::PCMA) => self.pcma_encoder.encode(&pcm),
214                    Some(CodecType::G722) => self
215                        .g722_encoder
216                        .get_or_insert_with(|| Box::new(G722Encoder::new()))
217                        .encode(&pcm),
218                    Some(CodecType::G729) => self
219                        .g729_encoder
220                        .get_or_insert_with(|| Box::new(G729Encoder::new()))
221                        .encode(&pcm),
222                    #[cfg(feature = "opus")]
223                    Some(CodecType::Opus) => self
224                        .opus_encoder
225                        .get_or_insert_with(OpusEncoder::new_default)
226                        .encode(&pcm),
227                    _ => samples_to_bytes(&pcm),
228                };
229                (payload_type, payload)
230            }
231            Samples::RTP {
232                payload_type,
233                payload,
234                ..
235            } => (payload_type, payload),
236            _ => (payload_type, vec![]),
237        }
238    }
239}
240
241#[cfg(test)]
242mod tests {
243    use super::*;
244
245    #[cfg(feature = "opus")]
246    #[test]
247    fn test_encode_dynamic_opus_payload_type_uses_opus_encoder() {
248        let mut codec = TrackCodec::new();
249        codec.set_payload_type(96, CodecType::Opus);
250
251        let frame = AudioFrame {
252            samples: Samples::PCM {
253                // 20ms @ 16k mono
254                samples: vec![0; 320],
255            },
256            sample_rate: 16000,
257            channels: 1,
258            ..Default::default()
259        };
260
261        let (pt, payload) = codec.encode(96, frame);
262        assert_eq!(pt, 96);
263        assert!(!payload.is_empty());
264        // If this were raw PCM bytes, it would be 640 bytes.
265        assert!(payload.len() < 640);
266    }
267}