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
8use crate::Error;
9/// opus_packet_get_samples_per_frame(toc, Fs).
10pub fn samples_per_frame(toc: u8, fs: i32) -> i32 {
11    if toc & 0x80 != 0 {
12        let a = ((toc >> 3) & 0x3) as i32;
13        (fs << a) / 400
14    } else if toc & 0x60 == 0x60 {
15        if toc & 0x08 != 0 { fs / 50 } else { fs / 100 }
16    } else {
17        let a = ((toc >> 3) & 0x3) as i32;
18        if a == 3 {
19            fs * 60 / 1000
20        } else {
21            (fs << a) / 100
22        }
23    }
24}
25
26/// opus_packet_get_nb_frames.
27///
28/// # Errors
29///
30/// [`Error::BadArg`] for an empty packet; [`Error::InvalidPacket`] if the
31/// frame-count byte of a code-3 packet is missing.
32pub fn nb_frames(packet: &[u8]) -> Result<i32, Error> {
33    if packet.is_empty() {
34        return Err(Error::BadArg("bad arg"));
35    }
36    match packet[0] & 0x3 {
37        0 => Ok(1),
38        3 => {
39            if packet.len() < 2 {
40                Err(Error::InvalidPacket("invalid packet"))
41            } else {
42                Ok((packet[1] & 0x3f) as i32)
43            }
44        }
45        _ => Ok(2),
46    }
47}
48
49fn parse_size(data: &[u8]) -> (i32, i32) {
50    // returns (bytes_consumed, size); size<0 => error
51    if data.is_empty() {
52        (-1, -1)
53    } else if data[0] < 252 {
54        (1, data[0] as i32)
55    } else if data.len() < 2 {
56        (-1, -1)
57    } else {
58        (2, data[1] as i32 * 4 + data[0] as i32)
59    }
60}
61
62#[cfg(test)]
63fn encode_size(size: i32, out: &mut Vec<u8>) {
64    if size < 252 {
65        out.push(size as u8);
66    } else {
67        let b0 = 252 + (size & 0x3);
68        out.push(b0 as u8);
69        out.push(((size - b0) >> 2) as u8);
70    }
71}
72
73/// Most frames one packet can carry: 120 ms of 2.5 ms frames (RFC 6716 3.2.5).
74const MAX_FRAMES: usize = 48;
75
76const TOO_SMALL: Error = Error::BufferTooSmall("output buffer too small for packet");
77
78/// Bounds-checked writer over a caller's buffer.
79struct Cursor<'a> {
80    buf: &'a mut [u8],
81    pos: usize,
82}
83
84impl Cursor<'_> {
85    fn put(&mut self, b: u8) -> Result<(), Error> {
86        *self.buf.get_mut(self.pos).ok_or(TOO_SMALL)? = b;
87        self.pos += 1;
88        Ok(())
89    }
90
91    fn put_all(&mut self, src: &[u8]) -> Result<(), Error> {
92        let end = self.pos + src.len();
93        self.buf
94            .get_mut(self.pos..end)
95            .ok_or(TOO_SMALL)?
96            .copy_from_slice(src);
97        self.pos = end;
98        Ok(())
99    }
100
101    /// One- or two-byte frame length (RFC 6716 3.2.1).
102    fn put_size(&mut self, size: usize) -> Result<(), Error> {
103        if size < 252 {
104            self.put(size as u8)
105        } else {
106            let b0 = 252 + (size & 0x3);
107            self.put(b0 as u8)?;
108            self.put(((size - b0) >> 2) as u8)
109        }
110    }
111}
112
113/// Fixed-capacity list of frame sizes (parsing allocates nothing).
114#[derive(Clone, Copy)]
115struct SizeList {
116    v: [i32; MAX_FRAMES],
117    n: usize,
118}
119
120impl SizeList {
121    const fn new() -> Self {
122        Self {
123            v: [0; MAX_FRAMES],
124            n: 0,
125        }
126    }
127
128    fn push(&mut self, size: i32) -> Result<(), Error> {
129        *self
130            .v
131            .get_mut(self.n)
132            .ok_or(Error::InvalidPacket("invalid packet"))? = size;
133        self.n += 1;
134        Ok(())
135    }
136
137    fn clear(&mut self) {
138        self.n = 0;
139    }
140
141    fn as_slice(&self) -> &[i32] {
142        &self.v[..self.n]
143    }
144}
145
146/// A parsed packet: TOC, frame byte-ranges, and where the packet ends.
147pub(crate) struct Frames {
148    pub(crate) toc: u8,
149    pub(crate) count: usize,
150    pub(crate) ranges: [(usize, usize); MAX_FRAMES],
151    pub(crate) end: usize,
152}
153
154impl Frames {
155    pub(crate) fn ranges(&self) -> &[(usize, usize)] {
156        &self.ranges[..self.count]
157    }
158}
159
160/// Split `data` into its frames. Returns (toc, frame byte-ranges, packet_offset).
161/// `self_delimited` parses the trailing length prefix used by multistream.
162#[allow(clippy::type_complexity)]
163///
164/// # Errors
165///
166/// [`Error::InvalidPacket`] if the packet violates RFC 6716 ยง3 framing
167/// (truncated lengths, frame sizes beyond the payload, bad padding).
168pub fn parse_packet(
169    data: &[u8],
170    self_delimited: bool,
171) -> Result<(u8, Vec<(usize, usize)>, usize), Error> {
172    let f = parse_frames(data, self_delimited)?;
173    Ok((f.toc, f.ranges().to_vec(), f.end))
174}
175
176/// Allocation-free [`parse_packet`]: the frame table lives on the stack.
177pub(crate) fn parse_frames(data: &[u8], self_delimited: bool) -> Result<Frames, Error> {
178    if data.is_empty() {
179        return Err(Error::InvalidPacket("invalid packet"));
180    }
181    let framesize = samples_per_frame(data[0], 48000);
182    let toc = data[0];
183    let mut pos = 1usize; // cursor into data
184    let mut len = data.len() as i32 - 1;
185    let mut cbr = false;
186    let mut last_size = len;
187    let mut sizes = SizeList::new();
188
189    let count: usize = match toc & 0x3 {
190        0 => 1,
191        1 => {
192            cbr = true;
193            if !self_delimited {
194                if len & 1 != 0 {
195                    return Err(Error::InvalidPacket("invalid packet"));
196                }
197                last_size = len / 2;
198                sizes.push(last_size)?;
199            }
200            2
201        }
202        2 => {
203            let (bytes, sz) = parse_size(&data[pos..]);
204            if bytes < 0 {
205                return Err(Error::InvalidPacket("invalid packet"));
206            }
207            len -= bytes;
208            if sz < 0 || sz > len {
209                return Err(Error::InvalidPacket("invalid packet"));
210            }
211            pos += bytes as usize;
212            sizes.push(sz)?;
213            last_size = len - sz;
214            2
215        }
216        _ => {
217            if len < 1 {
218                return Err(Error::InvalidPacket("invalid packet"));
219            }
220            let ch = data[pos];
221            pos += 1;
222            len -= 1;
223            let count = (ch & 0x3f) as usize;
224            if count == 0 || framesize * count as i32 > 5760 {
225                return Err(Error::InvalidPacket("invalid packet"));
226            }
227            if ch & 0x40 != 0 {
228                // padding
229                loop {
230                    if len <= 0 {
231                        return Err(Error::InvalidPacket("invalid packet"));
232                    }
233                    let p = data[pos];
234                    pos += 1;
235                    len -= 1;
236                    let tmp = if p == 255 { 254 } else { p as i32 };
237                    len -= tmp;
238                    if p != 255 {
239                        break;
240                    }
241                }
242            }
243            if len < 0 {
244                return Err(Error::InvalidPacket("invalid packet"));
245            }
246            cbr = ch & 0x80 == 0;
247            if !cbr {
248                last_size = len;
249                for _ in 0..count - 1 {
250                    let (bytes, sz) = parse_size(&data[pos..]);
251                    if bytes < 0 {
252                        return Err(Error::InvalidPacket("invalid packet"));
253                    }
254                    len -= bytes;
255                    if sz < 0 || sz > len {
256                        return Err(Error::InvalidPacket("invalid packet"));
257                    }
258                    pos += bytes as usize;
259                    sizes.push(sz)?;
260                    last_size -= bytes + sz;
261                }
262                if last_size < 0 {
263                    return Err(Error::InvalidPacket("invalid packet"));
264                }
265            } else if !self_delimited {
266                last_size = len / count as i32;
267                if last_size * count as i32 != len {
268                    return Err(Error::InvalidPacket("invalid packet"));
269                }
270                for _ in 0..count - 1 {
271                    sizes.push(last_size)?;
272                }
273            }
274            count
275        }
276    };
277
278    if self_delimited {
279        let (bytes, sz) = parse_size(&data[pos..]);
280        if bytes < 0 {
281            return Err(Error::InvalidPacket("invalid packet"));
282        }
283        len -= bytes;
284        if sz < 0 || sz > len {
285            return Err(Error::InvalidPacket("invalid packet"));
286        }
287        pos += bytes as usize;
288        if cbr {
289            if sz * count as i32 > len {
290                return Err(Error::InvalidPacket("invalid packet"));
291            }
292            sizes.clear();
293            for _ in 0..count - 1 {
294                sizes.push(sz)?;
295            }
296            sizes.push(sz)?;
297        } else {
298            if bytes + sz > last_size {
299                return Err(Error::InvalidPacket("invalid packet"));
300            }
301            sizes.push(sz)?;
302        }
303    } else {
304        if last_size > 1275 {
305            return Err(Error::InvalidPacket("invalid packet"));
306        }
307        sizes.push(last_size)?;
308    }
309
310    // Frame byte-ranges start at `pos`.
311    let mut ranges = [(0usize, 0usize); MAX_FRAMES];
312    let mut off = pos;
313    for (slot, &s) in ranges.iter_mut().zip(sizes.as_slice()) {
314        if off + s as usize > data.len() {
315            return Err(Error::InvalidPacket("invalid packet"));
316        }
317        *slot = (off, s as usize);
318        off += s as usize;
319    }
320    // `end` is where a self-delimited multistream packet's next stream begins.
321    Ok(Frames {
322        toc,
323        count: sizes.as_slice().len(),
324        ranges,
325        end: off,
326    })
327}
328
329/// opus_repacketizer: accumulate frames from one or more same-config packets,
330/// then emit them as a single re-framed packet.
331///
332/// Frame payloads are held back to back in one buffer. [`Repacketizer::reset`]
333/// keeps its capacity, so a reused repacketizer that writes with
334/// [`Repacketizer::out_into`] performs no allocation once warmed up.
335#[derive(Default)]
336pub struct Repacketizer {
337    toc: u8,
338    framesize: i32,
339    data: Vec<u8>,
340    /// `(start, len)` of each frame in `data`.
341    frames: Vec<(usize, usize)>,
342}
343
344impl Repacketizer {
345    /// An empty repacketizer.
346    pub fn new() -> Self {
347        Self::default()
348    }
349
350    /// Drop all held frames, keeping allocated capacity (opus_repacketizer_init).
351    pub fn reset(&mut self) {
352        self.data.clear();
353        self.frames.clear();
354    }
355
356    /// Number of frames added so far.
357    pub fn nb_frames(&self) -> usize {
358        self.frames.len()
359    }
360
361    fn frame(&self, i: usize) -> &[u8] {
362        let (start, len) = self.frames[i];
363        &self.data[start..start + len]
364    }
365
366    /// Append the frames of `data` (opus_repacketizer_cat). Errors if the TOC
367    /// config differs from frames already held, or the 120 ms cap is exceeded.
368    ///
369    /// # Errors
370    ///
371    /// [`Error::InvalidPacket`] if `data` is malformed, its TOC configuration
372    /// differs from the packets already added, or the total would exceed 120 ms.
373    pub fn cat(&mut self, data: &[u8]) -> Result<(), Error> {
374        self.cat_impl(data, false)
375    }
376
377    fn cat_impl(&mut self, data: &[u8], self_delimited: bool) -> Result<(), Error> {
378        if data.is_empty() {
379            return Err(Error::InvalidPacket("invalid packet"));
380        }
381        if self.frames.is_empty() {
382            self.toc = data[0];
383            self.framesize = samples_per_frame(data[0], 8000);
384        } else if self.toc & 0xfc != data[0] & 0xfc {
385            return Err(Error::InvalidPacket("toc mismatch"));
386        }
387        let curr = nb_frames(data)?;
388        if curr < 1 {
389            return Err(Error::InvalidPacket("invalid packet"));
390        }
391        if (curr as usize + self.frames.len()) as i32 * self.framesize > 960 {
392            return Err(Error::InvalidPacket("packet exceeds 120 ms"));
393        }
394        let parsed = parse_frames(data, self_delimited)?;
395        for &(o, l) in parsed.ranges() {
396            self.frames.push((self.data.len(), l));
397            self.data.extend_from_slice(&data[o..o + l]);
398        }
399        Ok(())
400    }
401
402    /// Emit frames [begin, end) as one packet (opus_repacketizer_out_range).
403    ///
404    /// # Errors
405    ///
406    /// [`Error::BadArg`] if `begin..end` is empty or outside the stored frames.
407    pub fn out_range(&self, begin: usize, end: usize) -> Result<Vec<u8>, Error> {
408        self.out_vec(begin, end, None, false)
409    }
410
411    /// Emit all held frames (opus_repacketizer_out).
412    ///
413    /// # Errors
414    ///
415    /// [`Error::BadArg`] if no frames have been added.
416    pub fn out(&self) -> Result<Vec<u8>, Error> {
417        self.out_vec(0, self.frames.len(), None, false)
418    }
419
420    /// Emit all held frames into `out`, returning the packet length
421    /// (opus_repacketizer_out with a caller buffer). Allocation-free.
422    ///
423    /// # Errors
424    ///
425    /// [`Error::BadArg`] if no frames have been added;
426    /// [`Error::BufferTooSmall`] if the packet does not fit in `out`.
427    pub fn out_into(&self, out: &mut [u8]) -> Result<usize, Error> {
428        self.write_range(0, self.frames.len(), None, false, out)
429    }
430
431    /// Emit frames [begin, end) into `out`, returning the packet length.
432    /// Allocation-free.
433    ///
434    /// # Errors
435    ///
436    /// [`Error::BadArg`] if `begin..end` is empty or outside the stored frames;
437    /// [`Error::BufferTooSmall`] if the packet does not fit in `out`.
438    pub fn out_range_into(&self, begin: usize, end: usize, out: &mut [u8]) -> Result<usize, Error> {
439        self.write_range(begin, end, None, false, out)
440    }
441
442    /// Emit all held frames padded to `pad_to` bytes (when larger than the
443    /// unpadded packet) into `out`. Allocation-free.
444    pub(crate) fn out_padded_into(&self, pad_to: usize, out: &mut [u8]) -> Result<usize, Error> {
445        self.write_range(0, self.frames.len(), Some(pad_to), false, out)
446    }
447
448    /// Emit all frames with the self-delimited framing multistream uses (the
449    /// last frame's length is coded so the packet's total size is derivable).
450    ///
451    /// # Errors
452    ///
453    /// [`Error::BadArg`] if no frames have been added.
454    pub fn out_self_delimited(&self) -> Result<Vec<u8>, Error> {
455        self.out_vec(0, self.frames.len(), None, true)
456    }
457
458    /// `Vec` front end to [`Repacketizer::write_range`], sized to an upper bound.
459    fn out_vec(
460        &self,
461        begin: usize,
462        end: usize,
463        pad_to: Option<usize>,
464        self_delimited: bool,
465    ) -> Result<Vec<u8>, Error> {
466        if begin >= end || end > self.frames.len() {
467            return Err(Error::BadArg("bad arg"));
468        }
469        let payload: usize = self.frames[begin..end].iter().map(|&(_, l)| l).sum();
470        // TOC + count byte, two bytes per length field (plus the self-delimited
471        // one), the payload, and any padding with its length bytes.
472        let bound = 2 + 2 * (end - begin + 1) + payload + pad_to.map_or(0, |n| n + n / 255 + 1);
473        let mut out = vec![0u8; bound];
474        let n = self.write_range(begin, end, pad_to, self_delimited, &mut out)?;
475        out.truncate(n);
476        Ok(out)
477    }
478
479    /// The one packet writer behind every output method.
480    fn write_range(
481        &self,
482        begin: usize,
483        end: usize,
484        pad_to: Option<usize>,
485        self_delimited: bool,
486        out: &mut [u8],
487    ) -> Result<usize, Error> {
488        if begin >= end || end > self.frames.len() {
489            return Err(Error::BadArg("bad arg"));
490        }
491        let count = end - begin;
492        let lens = &self.frames[begin..end];
493        let len = |i: usize| lens[i].1;
494        let mut w = Cursor { buf: out, pos: 0 };
495
496        if count > 2 || pad_to.is_some() {
497            // Code 3 (needed for >2 frames, or to carry padding).
498            let vbr = lens.iter().any(|&(_, l)| l != len(0));
499            w.put((self.toc & 0xfc) | 0x3)?;
500            w.put(count as u8 | if vbr { 0x80 } else { 0 })?;
501            // Current size, to know the padding amount.
502            let mut tot = 2usize;
503            if vbr {
504                for &(_, l) in &lens[..count - 1] {
505                    tot += 1 + usize::from(l >= 252) + l;
506                }
507                tot += len(count - 1);
508            } else {
509                tot += count * len(0);
510            }
511            let pad_amount = pad_to.map_or(0, |n| n.saturating_sub(tot));
512            if pad_amount != 0 {
513                w.buf[1] |= 0x40; // padding flag
514                let nb_255s = (pad_amount - 1) / 255;
515                for _ in 0..nb_255s {
516                    w.put(255)?;
517                }
518                w.put((pad_amount - 255 * nb_255s - 1) as u8)?;
519            }
520            if vbr {
521                for &(_, l) in &lens[..count - 1] {
522                    w.put_size(l)?;
523                }
524            }
525        } else if count == 1 {
526            w.put(self.toc & 0xfc)?; // code 0
527        } else if len(0) == len(1) {
528            w.put((self.toc & 0xfc) | 0x1)?; // code 1
529        } else {
530            w.put((self.toc & 0xfc) | 0x2)?; // code 2
531            w.put_size(len(0))?;
532        }
533        if self_delimited {
534            w.put_size(len(count - 1))?;
535        }
536        for i in begin..end {
537            w.put_all(self.frame(i))?;
538        }
539        if let Some(n) = pad_to {
540            while w.pos < n {
541                w.put(0)?;
542            }
543        }
544        Ok(w.pos)
545    }
546}
547
548/// opus_packet_pad: grow `packet` in place to `new_len` bytes by adding opus
549/// padding (no re-encode). No-op if already `new_len`; errors if `new_len` is
550/// smaller.
551///
552/// # Errors
553///
554/// [`Error::BadArg`] if `packet` is empty or `new_len` is smaller than it;
555/// [`Error::InvalidPacket`] if `packet` cannot be parsed.
556pub fn pad_packet(packet: &mut Vec<u8>, new_len: usize) -> Result<(), Error> {
557    if packet.is_empty() {
558        return Err(Error::BadArg("bad arg"));
559    }
560    if packet.len() == new_len {
561        return Ok(());
562    }
563    if packet.len() > new_len {
564        return Err(Error::BadArg("bad arg"));
565    }
566    let mut rp = Repacketizer::new();
567    rp.cat(packet)?;
568    let padded = rp.out_vec(0, rp.nb_frames(), Some(new_len), false)?;
569    *packet = padded;
570    Ok(())
571}
572
573/// opus_packet_unpad: strip opus padding, returning the minimal packet.
574///
575/// # Errors
576///
577/// [`Error::BadArg`] for an empty packet; [`Error::InvalidPacket`] if it
578/// cannot be parsed.
579pub fn unpad_packet(packet: &[u8]) -> Result<Vec<u8>, Error> {
580    if packet.is_empty() {
581        return Err(Error::BadArg("bad arg"));
582    }
583    let mut rp = Repacketizer::new();
584    rp.cat(packet)?;
585    rp.out()
586}
587
588#[cfg(test)]
589mod tests {
590    use super::*;
591
592    // Build a synthetic code-3 VBR packet with 3 frames of distinct lengths,
593    // split via out_range, and re-merge -> byte-identical (round-trip fidelity).
594    #[test]
595    fn split_merge_roundtrip() {
596        // toc config 12 (hybrid SWB 10ms) stereo bit off, code 3.
597        let toc = 12u8 << 3;
598        let mut pkt = vec![toc | 0x3, 3 | 0x80]; // code 3, vbr, count 3
599        let f0 = vec![0xAAu8; 3];
600        let f1 = vec![0xBBu8; 5];
601        let f2 = vec![0xCCu8; 4];
602        encode_size(3, &mut pkt);
603        encode_size(5, &mut pkt);
604        pkt.extend_from_slice(&f0);
605        pkt.extend_from_slice(&f1);
606        pkt.extend_from_slice(&f2);
607
608        let mut rp = Repacketizer::new();
609        rp.cat(&pkt).unwrap();
610        assert_eq!(rp.nb_frames(), 3);
611        // out() must reproduce the exact same packet.
612        assert_eq!(rp.out().unwrap(), pkt);
613        // Splitting single frames yields code-0 packets with the frame bytes.
614        let s0 = rp.out_range(0, 1).unwrap();
615        assert_eq!(s0[0] & 0x3, 0);
616        assert_eq!(&s0[1..], &f0[..]);
617        let s1 = rp.out_range(1, 2).unwrap();
618        assert_eq!(&s1[1..], &f1[..]);
619    }
620
621    #[test]
622    fn pad_unpad_identity() {
623        let toc = 8u8 << 3; // silk WB code 0
624        let mut pkt = vec![toc];
625        pkt.extend_from_slice(&[1, 2, 3, 4, 5]);
626        let orig = pkt.clone();
627        pad_packet(&mut pkt, orig.len() + 10).unwrap();
628        assert_eq!(pkt.len(), orig.len() + 10);
629        let back = unpad_packet(&pkt).unwrap();
630        // frame bytes recovered
631        let (_t, f, _) = parse_packet(&back, false).unwrap();
632        assert_eq!(&back[f[0].0..f[0].0 + f[0].1], &orig[1..]);
633    }
634
635    #[test]
636    fn cbr_merge_code1() {
637        // Two equal-length frames merge to code 1.
638        let toc = 8u8 << 3;
639        let p = vec![toc, 9, 9, 9]; // code 0, 3-byte frame
640        let mut rp = Repacketizer::new();
641        rp.cat(&p).unwrap();
642        rp.cat(&p).unwrap();
643        let out = rp.out().unwrap();
644        assert_eq!(out[0] & 0x3, 1); // code 1 (equal sizes)
645        assert_eq!(rp.nb_frames(), 2);
646    }
647}
648
649#[cfg(test)]
650mod sd_tests {
651    use super::*;
652    #[test]
653    fn self_delimited_roundtrip() {
654        // 3-frame vbr packet -> self-delimited -> parse(self_delimited) recovers frames.
655        let toc = 12u8 << 3;
656        let mut rp = Repacketizer::new();
657        let mut p = vec![toc | 0x3, 3 | 0x80];
658        encode_size(3, &mut p);
659        encode_size(5, &mut p);
660        p.extend_from_slice(&[1u8; 3]);
661        p.extend_from_slice(&[2u8; 5]);
662        p.extend_from_slice(&[3u8; 4]);
663        rp.cat(&p).unwrap();
664        let sd = rp.out_self_delimited().unwrap();
665        // append trailing bytes to simulate concatenation; parse must stop at packet_offset
666        let mut stream = sd.clone();
667        stream.extend_from_slice(&[0xEE; 7]);
668        let (t, frames, off) = parse_packet(&stream, true).unwrap();
669        assert_eq!(t, toc | 0x3);
670        assert_eq!(frames.len(), 3);
671        assert_eq!(&stream[frames[0].0..frames[0].0 + frames[0].1], &[1, 1, 1]);
672        assert_eq!(
673            &stream[frames[2].0..frames[2].0 + frames[2].1],
674            &[3, 3, 3, 3]
675        );
676        assert_eq!(off, sd.len()); // packet ends exactly at the SD boundary
677    }
678}