Skip to main content

rusty_opus/
repacketizer.rs

1//! Port of libopus `src/repacketizer.c` + the packet helpers from `src/opus.c`:
2//! split Opus packets into frames and recombine/re-frame/pad them WITHOUT
3//! re-encoding. Used to merge several packets into a longer one, split a
4//! multi-frame packet, or pad a packet to a target size (e.g. for CBR
5//! transport). All frames in a repacketizer must share the same TOC config
6//! (mode/bandwidth/frame-size); only the code (0..3) and framing change.
7
8/// opus_packet_get_samples_per_frame(toc, Fs).
9pub fn samples_per_frame(toc: u8, fs: i32) -> i32 {
10    if toc & 0x80 != 0 {
11        let a = ((toc >> 3) & 0x3) as i32;
12        (fs << a) / 400
13    } else if toc & 0x60 == 0x60 {
14        if toc & 0x08 != 0 {
15            fs / 50
16        } else {
17            fs / 100
18        }
19    } else {
20        let a = ((toc >> 3) & 0x3) as i32;
21        if a == 3 {
22            fs * 60 / 1000
23        } else {
24            (fs << a) / 100
25        }
26    }
27}
28
29/// opus_packet_get_nb_frames.
30pub fn nb_frames(packet: &[u8]) -> Result<i32, &'static str> {
31    if packet.is_empty() {
32        return Err("bad arg");
33    }
34    match packet[0] & 0x3 {
35        0 => Ok(1),
36        3 => {
37            if packet.len() < 2 {
38                Err("invalid packet")
39            } else {
40                Ok((packet[1] & 0x3f) as i32)
41            }
42        }
43        _ => Ok(2),
44    }
45}
46
47fn parse_size(data: &[u8]) -> (i32, i32) {
48    // returns (bytes_consumed, size); size<0 => error
49    if data.is_empty() {
50        (-1, -1)
51    } else if data[0] < 252 {
52        (1, data[0] as i32)
53    } else if data.len() < 2 {
54        (-1, -1)
55    } else {
56        (2, data[1] as i32 * 4 + data[0] as i32)
57    }
58}
59
60fn encode_size(size: i32, out: &mut Vec<u8>) {
61    if size < 252 {
62        out.push(size as u8);
63    } else {
64        let b0 = 252 + (size & 0x3);
65        out.push(b0 as u8);
66        out.push(((size - b0) >> 2) as u8);
67    }
68}
69
70/// Split `data` into its frames. Returns (toc, frame byte-ranges, packet_offset).
71/// `self_delimited` parses the trailing length prefix used by multistream.
72#[allow(clippy::type_complexity)]
73pub fn parse_packet(
74    data: &[u8],
75    self_delimited: bool,
76) -> Result<(u8, Vec<(usize, usize)>, usize), &'static str> {
77    if data.is_empty() {
78        return Err("invalid packet");
79    }
80    let framesize = samples_per_frame(data[0], 48000);
81    let toc = data[0];
82    let mut pos = 1usize; // cursor into data
83    let mut len = data.len() as i32 - 1;
84    let mut cbr = false;
85    let mut last_size = len;
86    let mut sizes: Vec<i32> = Vec::new();
87
88    let count: usize = match toc & 0x3 {
89        0 => 1,
90        1 => {
91            cbr = true;
92            if !self_delimited {
93                if len & 1 != 0 {
94                    return Err("invalid packet");
95                }
96                last_size = len / 2;
97                sizes.push(last_size);
98            }
99            2
100        }
101        2 => {
102            let (bytes, sz) = parse_size(&data[pos..]);
103            if bytes < 0 {
104                return Err("invalid packet");
105            }
106            len -= bytes;
107            if sz < 0 || sz > len {
108                return Err("invalid packet");
109            }
110            pos += bytes as usize;
111            sizes.push(sz);
112            last_size = len - sz;
113            2
114        }
115        _ => {
116            if len < 1 {
117                return Err("invalid packet");
118            }
119            let ch = data[pos];
120            pos += 1;
121            len -= 1;
122            let count = (ch & 0x3f) as usize;
123            if count == 0 || framesize * count as i32 > 5760 {
124                return Err("invalid packet");
125            }
126            if ch & 0x40 != 0 {
127                // padding
128                loop {
129                    if len <= 0 {
130                        return Err("invalid packet");
131                    }
132                    let p = data[pos];
133                    pos += 1;
134                    len -= 1;
135                    let tmp = if p == 255 { 254 } else { p as i32 };
136                    len -= tmp;
137                    if p != 255 {
138                        break;
139                    }
140                }
141            }
142            if len < 0 {
143                return Err("invalid packet");
144            }
145            cbr = ch & 0x80 == 0;
146            if !cbr {
147                last_size = len;
148                for _ in 0..count - 1 {
149                    let (bytes, sz) = parse_size(&data[pos..]);
150                    if bytes < 0 {
151                        return Err("invalid packet");
152                    }
153                    len -= bytes;
154                    if sz < 0 || sz > len {
155                        return Err("invalid packet");
156                    }
157                    pos += bytes as usize;
158                    sizes.push(sz);
159                    last_size -= bytes + sz;
160                }
161                if last_size < 0 {
162                    return Err("invalid packet");
163                }
164            } else if !self_delimited {
165                last_size = len / count as i32;
166                if last_size * count as i32 != len {
167                    return Err("invalid packet");
168                }
169                for _ in 0..count - 1 {
170                    sizes.push(last_size);
171                }
172            }
173            count
174        }
175    };
176
177    if self_delimited {
178        let (bytes, sz) = parse_size(&data[pos..]);
179        if bytes < 0 {
180            return Err("invalid packet");
181        }
182        len -= bytes;
183        if sz < 0 || sz > len {
184            return Err("invalid packet");
185        }
186        pos += bytes as usize;
187        if cbr {
188            if sz * count as i32 > len {
189                return Err("invalid packet");
190            }
191            sizes.clear();
192            for _ in 0..count - 1 {
193                sizes.push(sz);
194            }
195            sizes.push(sz);
196        } else {
197            if bytes + sz > last_size {
198                return Err("invalid packet");
199            }
200            sizes.push(sz);
201        }
202    } else {
203        if last_size > 1275 {
204            return Err("invalid packet");
205        }
206        sizes.push(last_size);
207    }
208
209    // Frame byte-ranges start at `pos`.
210    let mut frames = Vec::with_capacity(count);
211    let mut off = pos;
212    for &s in &sizes {
213        if off + s as usize > data.len() {
214            return Err("invalid packet");
215        }
216        frames.push((off, s as usize));
217        off += s as usize;
218    }
219    let packet_offset = off; // for self-delimited multistream advancement
220    Ok((toc, frames, packet_offset))
221}
222
223/// opus_repacketizer: accumulate frames from one or more same-config packets,
224/// then emit them as a single re-framed packet.
225#[derive(Default)]
226pub struct Repacketizer {
227    toc: u8,
228    framesize: i32,
229    frames: Vec<Vec<u8>>,
230}
231
232impl Repacketizer {
233    pub fn new() -> Self {
234        Repacketizer::default()
235    }
236
237    pub fn nb_frames(&self) -> usize {
238        self.frames.len()
239    }
240
241    /// Append the frames of `data` (opus_repacketizer_cat). Errors if the TOC
242    /// config differs from frames already held, or the 120 ms cap is exceeded.
243    pub fn cat(&mut self, data: &[u8]) -> Result<(), &'static str> {
244        self.cat_impl(data, false)
245    }
246
247    fn cat_impl(&mut self, data: &[u8], self_delimited: bool) -> Result<(), &'static str> {
248        if data.is_empty() {
249            return Err("invalid packet");
250        }
251        if self.frames.is_empty() {
252            self.toc = data[0];
253            self.framesize = samples_per_frame(data[0], 8000);
254        } else if self.toc & 0xfc != data[0] & 0xfc {
255            return Err("toc mismatch");
256        }
257        let curr = nb_frames(data)?;
258        if curr < 1 {
259            return Err("invalid packet");
260        }
261        if (curr as usize + self.frames.len()) as i32 * self.framesize > 960 {
262            return Err("packet exceeds 120 ms");
263        }
264        let (_toc, ranges, _off) = parse_packet(data, self_delimited)?;
265        for (o, l) in ranges {
266            self.frames.push(data[o..o + l].to_vec());
267        }
268        Ok(())
269    }
270
271    /// Emit frames [begin, end) as one packet (opus_repacketizer_out_range).
272    pub fn out_range(&self, begin: usize, end: usize) -> Result<Vec<u8>, &'static str> {
273        self.out_range_impl(begin, end, None)
274    }
275
276    /// Emit all held frames (opus_repacketizer_out).
277    pub fn out(&self) -> Result<Vec<u8>, &'static str> {
278        self.out_range_impl(0, self.frames.len(), None)
279    }
280
281    fn out_range_impl(
282        &self,
283        begin: usize,
284        end: usize,
285        pad_to: Option<usize>,
286    ) -> Result<Vec<u8>, &'static str> {
287        self.out_range_full(begin, end, pad_to, false)
288    }
289
290    /// Emit all frames with the self-delimited framing multistream uses (the
291    /// last frame's length is coded so the packet's total size is derivable).
292    pub fn out_self_delimited(&self) -> Result<Vec<u8>, &'static str> {
293        self.out_range_full(0, self.frames.len(), None, true)
294    }
295
296    fn out_range_full(
297        &self,
298        begin: usize,
299        end: usize,
300        pad_to: Option<usize>,
301        self_delimited: bool,
302    ) -> Result<Vec<u8>, &'static str> {
303        if begin >= end || end > self.frames.len() {
304            return Err("bad arg");
305        }
306        let count = end - begin;
307        let lens: Vec<usize> = self.frames[begin..end].iter().map(|f| f.len()).collect();
308        let mut out: Vec<u8> = Vec::new();
309
310        if count == 1 {
311            out.push(self.toc & 0xfc); // code 0
312        } else if count == 2 && lens[0] == lens[1] {
313            out.push((self.toc & 0xfc) | 0x1); // code 1
314        } else if count == 2 {
315            out.push((self.toc & 0xfc) | 0x2); // code 2
316            encode_size(lens[0] as i32, &mut out);
317        }
318
319        let want_pad = pad_to.is_some();
320        if count > 2 || (want_pad && count <= 2) {
321            // Code 3 (needed for >2 frames, or to carry padding).
322            out.clear();
323            let vbr = lens.iter().any(|&l| l != lens[0]);
324            if vbr {
325                out.push((self.toc & 0xfc) | 0x3);
326                out.push((count as u8) | 0x80);
327            } else {
328                out.push((self.toc & 0xfc) | 0x3);
329                out.push(count as u8);
330            }
331            // Compute current size to know the padding amount.
332            let mut tot = 2usize;
333            if vbr {
334                for &l in lens.iter().take(count - 1) {
335                    tot += 1 + usize::from(l >= 252) + l;
336                }
337                tot += lens[count - 1];
338            } else {
339                tot += count * lens[0];
340            }
341            let pad_amount = pad_to.map(|n| n.saturating_sub(tot)).unwrap_or(0);
342            if pad_amount != 0 {
343                out[1] |= 0x40; // padding flag
344                let nb_255s = (pad_amount - 1) / 255;
345                for _ in 0..nb_255s {
346                    out.push(255);
347                }
348                out.push((pad_amount - 255 * nb_255s - 1) as u8);
349            }
350            if vbr {
351                for &l in lens.iter().take(count - 1) {
352                    encode_size(l as i32, &mut out);
353                }
354            }
355            if self_delimited {
356                encode_size(lens[count - 1] as i32, &mut out);
357            }
358            for f in &self.frames[begin..end] {
359                out.extend_from_slice(f);
360            }
361            if let Some(n) = pad_to {
362                while out.len() < n {
363                    out.push(0);
364                }
365            }
366            return Ok(out);
367        }
368
369        if self_delimited {
370            encode_size(lens[count - 1] as i32, &mut out);
371        }
372        for f in &self.frames[begin..end] {
373            out.extend_from_slice(f);
374        }
375        Ok(out)
376    }
377}
378
379/// opus_packet_pad: grow `packet` in place to `new_len` bytes by adding opus
380/// padding (no re-encode). No-op if already `new_len`; errors if `new_len` is
381/// smaller.
382pub fn pad_packet(packet: &mut Vec<u8>, new_len: usize) -> Result<(), &'static str> {
383    if packet.is_empty() {
384        return Err("bad arg");
385    }
386    if packet.len() == new_len {
387        return Ok(());
388    }
389    if packet.len() > new_len {
390        return Err("bad arg");
391    }
392    let mut rp = Repacketizer::new();
393    rp.cat(packet)?;
394    let padded = rp.out_range_impl(0, rp.nb_frames(), Some(new_len))?;
395    *packet = padded;
396    Ok(())
397}
398
399/// opus_packet_unpad: strip opus padding, returning the minimal packet.
400pub fn unpad_packet(packet: &[u8]) -> Result<Vec<u8>, &'static str> {
401    if packet.is_empty() {
402        return Err("bad arg");
403    }
404    let mut rp = Repacketizer::new();
405    rp.cat(packet)?;
406    rp.out_range_impl(0, rp.nb_frames(), None)
407}
408
409#[cfg(test)]
410mod tests {
411    use super::*;
412
413    // Build a synthetic code-3 VBR packet with 3 frames of distinct lengths,
414    // split via out_range, and re-merge -> byte-identical (round-trip fidelity).
415    #[test]
416    fn split_merge_roundtrip() {
417        // toc config 12 (hybrid SWB 10ms) stereo bit off, code 3.
418        let toc = 12u8 << 3;
419        let mut pkt = vec![toc | 0x3, 3 | 0x80]; // code 3, vbr, count 3
420        let f0 = vec![0xAAu8; 3];
421        let f1 = vec![0xBBu8; 5];
422        let f2 = vec![0xCCu8; 4];
423        encode_size(3, &mut pkt);
424        encode_size(5, &mut pkt);
425        pkt.extend_from_slice(&f0);
426        pkt.extend_from_slice(&f1);
427        pkt.extend_from_slice(&f2);
428
429        let mut rp = Repacketizer::new();
430        rp.cat(&pkt).unwrap();
431        assert_eq!(rp.nb_frames(), 3);
432        // out() must reproduce the exact same packet.
433        assert_eq!(rp.out().unwrap(), pkt);
434        // Splitting single frames yields code-0 packets with the frame bytes.
435        let s0 = rp.out_range(0, 1).unwrap();
436        assert_eq!(s0[0] & 0x3, 0);
437        assert_eq!(&s0[1..], &f0[..]);
438        let s1 = rp.out_range(1, 2).unwrap();
439        assert_eq!(&s1[1..], &f1[..]);
440    }
441
442    #[test]
443    fn pad_unpad_identity() {
444        let toc = 8u8 << 3; // silk WB code 0
445        let mut pkt = vec![toc];
446        pkt.extend_from_slice(&[1, 2, 3, 4, 5]);
447        let orig = pkt.clone();
448        pad_packet(&mut pkt, orig.len() + 10).unwrap();
449        assert_eq!(pkt.len(), orig.len() + 10);
450        let back = unpad_packet(&pkt).unwrap();
451        // frame bytes recovered
452        let (_t, f, _) = parse_packet(&back, false).unwrap();
453        assert_eq!(&back[f[0].0..f[0].0 + f[0].1], &orig[1..]);
454    }
455
456    #[test]
457    fn cbr_merge_code1() {
458        // Two equal-length frames merge to code 1.
459        let toc = 8u8 << 3;
460        let p = vec![toc, 9, 9, 9]; // code 0, 3-byte frame
461        let mut rp = Repacketizer::new();
462        rp.cat(&p).unwrap();
463        rp.cat(&p).unwrap();
464        let out = rp.out().unwrap();
465        assert_eq!(out[0] & 0x3, 1); // code 1 (equal sizes)
466        assert_eq!(rp.nb_frames(), 2);
467    }
468}
469
470#[cfg(test)]
471mod sd_tests {
472    use super::*;
473    #[test]
474    fn self_delimited_roundtrip() {
475        // 3-frame vbr packet -> self-delimited -> parse(self_delimited) recovers frames.
476        let toc = 12u8 << 3;
477        let mut rp = Repacketizer::new();
478        let mut p = vec![toc | 0x3, 3 | 0x80];
479        encode_size(3, &mut p); encode_size(5, &mut p);
480        p.extend_from_slice(&[1u8;3]); p.extend_from_slice(&[2u8;5]); p.extend_from_slice(&[3u8;4]);
481        rp.cat(&p).unwrap();
482        let sd = rp.out_self_delimited().unwrap();
483        // append trailing bytes to simulate concatenation; parse must stop at packet_offset
484        let mut stream = sd.clone(); stream.extend_from_slice(&[0xEE;7]);
485        let (t, frames, off) = parse_packet(&stream, true).unwrap();
486        assert_eq!(t, toc | 0x3);
487        assert_eq!(frames.len(), 3);
488        assert_eq!(&stream[frames[0].0..frames[0].0+frames[0].1], &[1,1,1]);
489        assert_eq!(&stream[frames[2].0..frames[2].0+frames[2].1], &[3,3,3,3]);
490        assert_eq!(off, sd.len()); // packet ends exactly at the SD boundary
491    }
492}