Skip to main content

active_call/media/track/
track_codec.rs

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