Skip to main content

oxideav_opus/
frames.rs

1//! Opus packet frame-packing parser (RFC 6716 §3.2).
2//!
3//! The TOC byte (§3.1, decoded by [`crate::toc::OpusTocByte`]) determines
4//! how the rest of the packet is sliced into compressed Opus frames:
5//!
6//! * **Code 0** (§3.2.2) — one frame, the remaining `N - 1` bytes.
7//! * **Code 1** (§3.2.3) — two equal-size frames; `(N - 1)` MUST be even
8//!   (requirement R3), each frame is `(N - 1) / 2` bytes.
9//! * **Code 2** (§3.2.4) — two frames; a one- or two-byte §3.2.1
10//!   length sequence gives the size of the first frame, the rest is the
11//!   second frame (requirement R4).
12//! * **Code 3** (§3.2.5) — a signalled frame count `M` plus optional
13//!   Opus padding and, for VBR, `M - 1` per-frame length sequences. The
14//!   final frame consumes whatever remains before the trailing padding.
15//!
16//! This module performs the §3.2 layer only. It returns the
17//! compressed-frame byte slices (borrowed from the input buffer) so the
18//! SILK / CELT decoders can be wired up against them in a subsequent
19//! round. Length zero is a legal §3.2.1 result (DTX / lost frame); such
20//! frames appear as empty slices in the returned list.
21
22use crate::toc::{FrameCountCode, OpusTocByte};
23use crate::Error;
24
25/// Maximum compressed frame length permitted by the §3.2.1 two-byte
26/// length encoding. RFC 6716 §3.2.1: "The maximum representable length
27/// is 255 \* 4 + 255 = 1275 bytes." Requirement R2 forbids any
28/// individual frame from exceeding this.
29pub const MAX_FRAME_BYTES: usize = 1275;
30
31/// Maximum number of frames a code-3 packet may signal. RFC 6716
32/// §3.2.5: "M MUST NOT be zero, and the audio duration contained
33/// within a packet MUST NOT exceed 120 ms \[R5\]. This limits the
34/// maximum frame count for any frame size to 48 (for 2.5 ms frames)".
35pub const MAX_FRAMES_PER_PACKET: u8 = 48;
36
37/// A fully-parsed Opus packet: TOC byte plus the per-frame slices
38/// recovered by walking the §3.2 frame-packing layer.
39///
40/// Frame slices borrow from the original packet buffer; the parsed
41/// packet therefore carries the same lifetime.
42#[derive(Debug, Clone, PartialEq, Eq)]
43pub struct OpusPacket<'a> {
44    /// Decoded TOC byte (RFC 6716 §3.1).
45    pub toc: OpusTocByte,
46    frames: Vec<&'a [u8]>,
47    /// Number of trailing Opus-padding bytes (§3.2.5). Always zero for
48    /// codes 0/1/2; non-zero only for a code-3 packet whose `p` bit is
49    /// set.
50    pub padding: usize,
51}
52
53impl<'a> OpusPacket<'a> {
54    /// Parse one complete Opus packet (TOC byte plus §3.2 frame packing).
55    ///
56    /// Returns [`Error::EmptyPacket`] if the buffer is empty (R1), or
57    /// [`Error::MalformedPacket`] if any other §3.2 requirement is
58    /// violated.
59    pub fn parse(packet: &'a [u8]) -> Result<Self, Error> {
60        if packet.is_empty() {
61            return Err(Error::EmptyPacket);
62        }
63        let toc = OpusTocByte::from_byte(packet[0]);
64        let body = &packet[1..];
65
66        let (frames, padding) = match toc.frame_count_code {
67            FrameCountCode::One => parse_code0(body)?,
68            FrameCountCode::TwoEqual => parse_code1(body)?,
69            FrameCountCode::TwoUnequal => parse_code2(body)?,
70            FrameCountCode::Arbitrary => parse_code3(body)?,
71        };
72
73        Ok(Self {
74            toc,
75            frames,
76            padding,
77        })
78    }
79
80    /// Compressed frame payloads in packet order.
81    ///
82    /// Each slice borrows from the original packet buffer. A zero-length
83    /// slice represents a §3.2.1 DTX / lost-frame marker.
84    pub fn frames(&self) -> &[&'a [u8]] {
85        &self.frames
86    }
87
88    /// Number of compressed frames in the packet (equals
89    /// `self.frames().len()`).
90    pub fn frame_count(&self) -> usize {
91        self.frames.len()
92    }
93
94    /// Build an [`OpusPacket`] from already-parsed components. This is
95    /// the construction path used by the Appendix-B self-delimited
96    /// parser (`framing_self_delim::parse_self_delimited`), which
97    /// derives the frame slices using the Appendix-B extra length
98    /// field rather than the §3.2 implicit-length rules. Crate-private
99    /// to keep external callers on [`OpusPacket::parse`] /
100    /// [`crate::framing_self_delim::parse_self_delimited`].
101    pub(crate) fn new_self_delim(toc: OpusTocByte, frames: Vec<&'a [u8]>, padding: usize) -> Self {
102        Self {
103            toc,
104            frames,
105            padding,
106        }
107    }
108}
109
110/// Decode a §3.2.1 length sequence at the start of `bytes`.
111///
112/// Returns `(length, consumed_byte_count)` on success. The first byte
113/// is interpreted per RFC 6716 §3.2.1:
114///
115/// * `0` — no frame (DTX / lost). Length 0, one byte consumed.
116/// * `1..=251` — that value in bytes. One byte consumed.
117/// * `252..=255` — a second byte is required; the total length is
118///   `(second * 4) + first`. Two bytes consumed.
119pub(crate) fn decode_length(bytes: &[u8]) -> Result<(usize, usize), Error> {
120    let first = *bytes.first().ok_or(Error::MalformedPacket)? as usize;
121    if first < 252 {
122        Ok((first, 1))
123    } else {
124        let second = *bytes.get(1).ok_or(Error::MalformedPacket)? as usize;
125        Ok((second * 4 + first, 2))
126    }
127}
128
129/// §3.2.2 Code 0: the entire body is one frame.
130///
131/// R2: the implicit length MUST NOT exceed 1275 bytes.
132fn parse_code0(body: &[u8]) -> Result<(Vec<&[u8]>, usize), Error> {
133    if body.len() > MAX_FRAME_BYTES {
134        return Err(Error::MalformedPacket);
135    }
136    Ok((vec![body], 0))
137}
138
139/// §3.2.3 Code 1: two equal-size frames.
140///
141/// R3: `(N - 1)` (= `body.len()`) MUST be even. Each frame is
142/// `body.len() / 2` bytes. R2 bounds each frame to ≤ 1275 bytes.
143fn parse_code1(body: &[u8]) -> Result<(Vec<&[u8]>, usize), Error> {
144    if body.len() % 2 != 0 {
145        return Err(Error::MalformedPacket);
146    }
147    let half = body.len() / 2;
148    if half > MAX_FRAME_BYTES {
149        return Err(Error::MalformedPacket);
150    }
151    Ok((vec![&body[..half], &body[half..]], 0))
152}
153
154/// §3.2.4 Code 2: two frames with an explicit length for the first.
155///
156/// R4: the packet must contain enough bytes after the TOC for a valid
157/// length, and the decoded `N1` must not exceed the bytes remaining
158/// after the length sequence.
159fn parse_code2(body: &[u8]) -> Result<(Vec<&[u8]>, usize), Error> {
160    let (n1, consumed) = decode_length(body)?;
161    if n1 > MAX_FRAME_BYTES {
162        return Err(Error::MalformedPacket);
163    }
164    let after_len = &body[consumed..];
165    if n1 > after_len.len() {
166        return Err(Error::MalformedPacket);
167    }
168    let frame1 = &after_len[..n1];
169    let frame2 = &after_len[n1..];
170    if frame2.len() > MAX_FRAME_BYTES {
171        return Err(Error::MalformedPacket);
172    }
173    Ok((vec![frame1, frame2], 0))
174}
175
176/// §3.2.5 Code 3: signalled frame count, optional padding, optional
177/// per-frame VBR lengths.
178///
179/// `body` starts at the frame-count byte (§3.1 placed the TOC byte
180/// already). Layout:
181///
182/// 1. Frame-count byte: bit 0 = `v` (VBR), bit 1 = `p` (padding),
183///    bits 2..=7 = `M` (frame count, 1..=48).
184/// 2. If `p` set: one or more padding-length bytes per §3.2.5; the
185///    sum of those bytes (with the §3.2.5 "255 means 254 + read next"
186///    chain) is the number of trailing padding bytes appended after
187///    the last frame. The header bytes that *encode* the padding
188///    length count toward the P budget per the RFC's definition of P
189///    (header bytes + padding bytes), so we deduct both halves from
190///    what remains before the frame data.
191/// 3. If `v` set: `M - 1` §3.2.1 length sequences; the final frame
192///    consumes whatever remains in the body after subtracting the
193///    padding.
194/// 4. Otherwise (CBR): every frame is `R / M` bytes where
195///    `R = N - 2 - P` (R6: R must be a non-negative multiple of M).
196fn parse_code3(body: &[u8]) -> Result<(Vec<&[u8]>, usize), Error> {
197    // R6/R7: a code-3 packet has at least the frame-count byte.
198    let fc = *body.first().ok_or(Error::MalformedPacket)?;
199    let v_bit = fc & 0x01 != 0;
200    let p_bit = fc & 0x02 != 0;
201    let m = fc >> 2;
202    if m == 0 || m > MAX_FRAMES_PER_PACKET {
203        return Err(Error::MalformedPacket);
204    }
205    let mut cursor = 1usize;
206
207    // §3.2.5 padding-length chain. Each byte 0..=254 contributes its
208    // value as padding bytes; 255 contributes 254 and demands another
209    // length byte. The header bytes used to encode the chain are part
210    // of P (the total bytes "added to the packet" budget), per the
211    // RFC's definition.
212    let mut padding_bytes: usize = 0;
213    let mut padding_header_bytes: usize = 0;
214    if p_bit {
215        loop {
216            let byte = *body.get(cursor).ok_or(Error::MalformedPacket)? as usize;
217            cursor += 1;
218            padding_header_bytes += 1;
219            if byte == 255 {
220                padding_bytes += 254;
221            } else {
222                padding_bytes += byte;
223                break;
224            }
225        }
226    }
227    let total_padding = padding_bytes + padding_header_bytes;
228    // R6/R7: P (header bytes + padding bytes themselves) MUST be no
229    // more than N - 2. Here body.len() = N - 1, so P MUST be no more
230    // than body.len() - 1.
231    if total_padding + 1 > body.len() {
232        return Err(Error::MalformedPacket);
233    }
234
235    let m = m as usize;
236    let mut frames: Vec<&[u8]> = Vec::with_capacity(m);
237
238    if v_bit {
239        // VBR: read M-1 lengths, then derive the last frame size from
240        // the remaining bytes after the padding tail.
241        let mut declared_lengths: Vec<usize> = Vec::with_capacity(m.saturating_sub(1));
242        for _ in 0..m.saturating_sub(1) {
243            let (n, consumed) = decode_length(&body[cursor..])?;
244            if n > MAX_FRAME_BYTES {
245                return Err(Error::MalformedPacket);
246            }
247            declared_lengths.push(n);
248            cursor += consumed;
249        }
250        // Bytes still owed: padding + sum(declared) + final-frame.
251        let remaining = body.len() - cursor;
252        let declared_sum: usize = declared_lengths.iter().copied().sum();
253        if declared_sum + padding_bytes > remaining {
254            return Err(Error::MalformedPacket);
255        }
256        let last_len = remaining - declared_sum - padding_bytes;
257        if last_len > MAX_FRAME_BYTES {
258            return Err(Error::MalformedPacket);
259        }
260        for n in declared_lengths {
261            frames.push(&body[cursor..cursor + n]);
262            cursor += n;
263        }
264        frames.push(&body[cursor..cursor + last_len]);
265    } else {
266        // CBR: R = N - 2 - P, frames are R/M bytes each. body.len()
267        // = N - 1, so R = body.len() - 1 - P. After accounting for
268        // the frame-count byte (already consumed at cursor==1) and
269        // any padding-header bytes (consumed during the chain) and
270        // padding_bytes (trailing), the remaining body slice is R.
271        let r = body.len() - cursor - padding_bytes;
272        if r % m != 0 {
273            return Err(Error::MalformedPacket);
274        }
275        let per = r / m;
276        if per > MAX_FRAME_BYTES {
277            return Err(Error::MalformedPacket);
278        }
279        for _ in 0..m {
280            frames.push(&body[cursor..cursor + per]);
281            cursor += per;
282        }
283    }
284
285    Ok((frames, padding_bytes))
286}
287
288#[cfg(test)]
289mod tests {
290    use super::*;
291    use crate::toc::{ChannelMapping, FrameCountCode};
292
293    fn toc(config: u8, stereo: bool, code: u8) -> u8 {
294        (config << 3) | ((stereo as u8) << 2) | (code & 0x03)
295    }
296
297    // ----- §3.2.1 length decoding -----
298
299    #[test]
300    fn length_single_byte_values_0_through_251() {
301        for value in [0u8, 1, 100, 200, 251] {
302            let (len, n) = decode_length(&[value]).unwrap();
303            assert_eq!(len, value as usize);
304            assert_eq!(n, 1);
305        }
306    }
307
308    #[test]
309    fn length_two_byte_boundary() {
310        // first=252 second=0 -> 0*4 + 252 = 252
311        let (len, n) = decode_length(&[252, 0]).unwrap();
312        assert_eq!(len, 252);
313        assert_eq!(n, 2);
314        // first=255 second=255 -> 255*4 + 255 = 1275 (= MAX_FRAME_BYTES)
315        let (len, n) = decode_length(&[255, 255]).unwrap();
316        assert_eq!(len, MAX_FRAME_BYTES);
317        assert_eq!(n, 2);
318        // first=253 second=10 -> 10*4 + 253 = 293
319        let (len, n) = decode_length(&[253, 10]).unwrap();
320        assert_eq!(len, 293);
321        assert_eq!(n, 2);
322    }
323
324    #[test]
325    fn length_two_byte_missing_second_rejects() {
326        assert_eq!(decode_length(&[252]), Err(Error::MalformedPacket));
327        assert_eq!(decode_length(&[255]), Err(Error::MalformedPacket));
328    }
329
330    #[test]
331    fn length_empty_rejects() {
332        assert_eq!(decode_length(&[]), Err(Error::MalformedPacket));
333    }
334
335    // ----- §3.2.2 Code 0 -----
336
337    #[test]
338    fn code0_single_frame_round_trip() {
339        // config=1 (SILK NB 20 ms), mono, c=0.
340        let mut packet = vec![toc(1, false, 0)];
341        packet.extend_from_slice(&[0xAA, 0xBB, 0xCC, 0xDD]);
342        let parsed = OpusPacket::parse(&packet).unwrap();
343        assert_eq!(parsed.frame_count(), 1);
344        assert_eq!(parsed.frames()[0], &[0xAA, 0xBB, 0xCC, 0xDD]);
345        assert_eq!(parsed.padding, 0);
346        assert_eq!(parsed.toc.frame_count_code, FrameCountCode::One);
347    }
348
349    #[test]
350    fn code0_toc_only_yields_empty_frame() {
351        // A code-0 packet with no payload is legal: §3.2.1 allows a
352        // zero-length frame (DTX / lost). Frames() therefore returns
353        // one empty slice.
354        let packet = [toc(1, false, 0)];
355        let parsed = OpusPacket::parse(&packet).unwrap();
356        assert_eq!(parsed.frame_count(), 1);
357        assert!(parsed.frames()[0].is_empty());
358    }
359
360    #[test]
361    fn code0_rejects_oversize_frame() {
362        let mut packet = vec![toc(1, false, 0)];
363        packet.resize(1 + MAX_FRAME_BYTES + 1, 0);
364        assert_eq!(OpusPacket::parse(&packet), Err(Error::MalformedPacket));
365    }
366
367    // ----- §3.2.3 Code 1 -----
368
369    #[test]
370    fn code1_two_equal_split_at_midpoint() {
371        let mut packet = vec![toc(1, false, 1)];
372        packet.extend_from_slice(&[0x11, 0x22, 0x33, 0x44, 0x55, 0x66]);
373        let parsed = OpusPacket::parse(&packet).unwrap();
374        assert_eq!(parsed.frame_count(), 2);
375        assert_eq!(parsed.frames()[0], &[0x11, 0x22, 0x33]);
376        assert_eq!(parsed.frames()[1], &[0x44, 0x55, 0x66]);
377        assert_eq!(parsed.toc.frame_count_code, FrameCountCode::TwoEqual);
378    }
379
380    #[test]
381    fn code1_rejects_odd_body_per_r3() {
382        let mut packet = vec![toc(1, false, 1)];
383        packet.extend_from_slice(&[0x11, 0x22, 0x33]);
384        assert_eq!(OpusPacket::parse(&packet), Err(Error::MalformedPacket));
385    }
386
387    #[test]
388    fn code1_toc_only_two_empty_frames() {
389        let packet = [toc(1, false, 1)];
390        let parsed = OpusPacket::parse(&packet).unwrap();
391        assert_eq!(parsed.frame_count(), 2);
392        assert!(parsed.frames()[0].is_empty());
393        assert!(parsed.frames()[1].is_empty());
394    }
395
396    // ----- §3.2.4 Code 2 -----
397
398    #[test]
399    fn code2_single_byte_length() {
400        // N1 = 3, second frame = 4 bytes.
401        let mut packet = vec![toc(1, false, 2), 3];
402        packet.extend_from_slice(&[0xA1, 0xA2, 0xA3]); // frame 1 (3 bytes)
403        packet.extend_from_slice(&[0xB1, 0xB2, 0xB3, 0xB4]); // frame 2
404        let parsed = OpusPacket::parse(&packet).unwrap();
405        assert_eq!(parsed.frame_count(), 2);
406        assert_eq!(parsed.frames()[0], &[0xA1, 0xA2, 0xA3]);
407        assert_eq!(parsed.frames()[1], &[0xB1, 0xB2, 0xB3, 0xB4]);
408    }
409
410    #[test]
411    fn code2_two_byte_length() {
412        // N1 = 4*4 + 252 = 268.
413        let n1 = 268usize;
414        let mut packet = vec![toc(1, false, 2), 252, 4];
415        packet.extend(std::iter::repeat(0xAA).take(n1));
416        packet.extend_from_slice(&[0xBB; 5]); // frame 2 = 5 bytes
417        let parsed = OpusPacket::parse(&packet).unwrap();
418        assert_eq!(parsed.frame_count(), 2);
419        assert_eq!(parsed.frames()[0].len(), n1);
420        assert!(parsed.frames()[0].iter().all(|&b| b == 0xAA));
421        assert_eq!(parsed.frames()[1], &[0xBB; 5]);
422    }
423
424    #[test]
425    fn code2_one_byte_packet_invalid() {
426        // RFC §3.2.4: "a 1-byte code 2 packet is always invalid".
427        let packet = [toc(1, false, 2)];
428        assert_eq!(OpusPacket::parse(&packet), Err(Error::MalformedPacket));
429    }
430
431    #[test]
432    fn code2_two_byte_packet_only_legal_when_lengths_are_zero() {
433        // body = [0] -> N1 = 0, second frame = 0. Legal.
434        let packet = [toc(1, false, 2), 0];
435        let parsed = OpusPacket::parse(&packet).unwrap();
436        assert_eq!(parsed.frame_count(), 2);
437        assert!(parsed.frames()[0].is_empty());
438        assert!(parsed.frames()[1].is_empty());
439
440        // body = [5] -> N1 = 5 but no bytes remain. Reject per R4.
441        let packet = [toc(1, false, 2), 5];
442        assert_eq!(OpusPacket::parse(&packet), Err(Error::MalformedPacket));
443
444        // body = [253] -> demands a second length byte that isn't there.
445        let packet = [toc(1, false, 2), 253];
446        assert_eq!(OpusPacket::parse(&packet), Err(Error::MalformedPacket));
447    }
448
449    #[test]
450    fn code2_n1_exceeds_remaining_rejected_per_r4() {
451        let mut packet = vec![toc(1, false, 2), 10];
452        // Only 5 bytes follow but N1 = 10.
453        packet.extend_from_slice(&[0; 5]);
454        assert_eq!(OpusPacket::parse(&packet), Err(Error::MalformedPacket));
455    }
456
457    // ----- §3.2.5 Code 3 -----
458
459    #[test]
460    fn code3_cbr_no_padding() {
461        // M = 3, vbr=0, p=0; three equal-size frames of 4 bytes each.
462        let m: u8 = 3;
463        let fc = m << 2; // v=0 p=0
464        let mut packet = vec![toc(15, false, 3), fc];
465        packet.extend_from_slice(&[0xA1, 0xA2, 0xA3, 0xA4]);
466        packet.extend_from_slice(&[0xB1, 0xB2, 0xB3, 0xB4]);
467        packet.extend_from_slice(&[0xC1, 0xC2, 0xC3, 0xC4]);
468        let parsed = OpusPacket::parse(&packet).unwrap();
469        assert_eq!(parsed.frame_count(), 3);
470        assert_eq!(parsed.frames()[0], &[0xA1, 0xA2, 0xA3, 0xA4]);
471        assert_eq!(parsed.frames()[1], &[0xB1, 0xB2, 0xB3, 0xB4]);
472        assert_eq!(parsed.frames()[2], &[0xC1, 0xC2, 0xC3, 0xC4]);
473        assert_eq!(parsed.padding, 0);
474    }
475
476    #[test]
477    fn code3_cbr_rejects_non_multiple_of_m_per_r6() {
478        // M = 3 but only 5 payload bytes -> not a multiple of 3.
479        let fc = 3 << 2;
480        let mut packet = vec![toc(15, false, 3), fc];
481        packet.extend_from_slice(&[0; 5]);
482        assert_eq!(OpusPacket::parse(&packet), Err(Error::MalformedPacket));
483    }
484
485    #[test]
486    fn code3_cbr_with_padding() {
487        // M = 2, p=1, v=0, padding chain = single byte "5" -> 5 trailing
488        // zero bytes. Then 2 frames of 3 bytes each.
489        let m: u8 = 2;
490        let fc = (m << 2) | 0b10; // p=1, v=0
491        let mut packet = vec![toc(15, false, 3), fc, 5];
492        packet.extend_from_slice(&[0xD1, 0xD2, 0xD3]);
493        packet.extend_from_slice(&[0xE1, 0xE2, 0xE3]);
494        packet.extend_from_slice(&[0x00; 5]);
495        let parsed = OpusPacket::parse(&packet).unwrap();
496        assert_eq!(parsed.frame_count(), 2);
497        assert_eq!(parsed.frames()[0], &[0xD1, 0xD2, 0xD3]);
498        assert_eq!(parsed.frames()[1], &[0xE1, 0xE2, 0xE3]);
499        assert_eq!(parsed.padding, 5);
500    }
501
502    #[test]
503    fn code3_vbr_no_padding() {
504        // M = 3, v=1, p=0. M-1 = 2 length sequences then last-frame
505        // is implicit. Frame sizes: 2, 4, 1.
506        let m: u8 = 3;
507        let fc = (m << 2) | 0b01;
508        let mut packet = vec![toc(15, false, 3), fc, 2, 4];
509        packet.extend_from_slice(&[0xA1, 0xA2]);
510        packet.extend_from_slice(&[0xB1, 0xB2, 0xB3, 0xB4]);
511        packet.push(0xC1);
512        let parsed = OpusPacket::parse(&packet).unwrap();
513        assert_eq!(parsed.frame_count(), 3);
514        assert_eq!(parsed.frames()[0], &[0xA1, 0xA2]);
515        assert_eq!(parsed.frames()[1], &[0xB1, 0xB2, 0xB3, 0xB4]);
516        assert_eq!(parsed.frames()[2], &[0xC1]);
517        assert_eq!(parsed.padding, 0);
518    }
519
520    #[test]
521    fn code3_vbr_with_padding() {
522        // Mirrors fixture: 4 VBR frames + 7 padding bytes.
523        // Sizes used in trace: 99, 77, 66, 68 -> total 310; +3
524        // length bytes (one each for the 3 declared) + frame-count
525        // byte + padding-length byte + 7 padding bytes + TOC.
526        // body (without TOC) = fc + pad_len + 3 lengths + 99 + 77 + 66
527        //                      + 68 + 7 pad = 1 + 1 + 3 + 310 + 7 = 322.
528        // Total packet = 323 bytes.
529        let m: u8 = 4;
530        let fc = (m << 2) | 0b11; // p=1, v=1
531        let pad_len = 7u8;
532        let sizes = [99u8, 77, 66, 68];
533        let mut packet = vec![toc(15, false, 3), fc, pad_len];
534        // M-1 = 3 declared lengths.
535        packet.extend_from_slice(&sizes[..3]);
536        for &n in sizes.iter() {
537            packet.extend(std::iter::repeat(0xAA).take(n as usize));
538        }
539        packet.extend_from_slice(&[0u8; 7]);
540        let parsed = OpusPacket::parse(&packet).unwrap();
541        assert_eq!(parsed.frame_count(), 4);
542        assert_eq!(parsed.frames()[0].len(), 99);
543        assert_eq!(parsed.frames()[1].len(), 77);
544        assert_eq!(parsed.frames()[2].len(), 66);
545        assert_eq!(parsed.frames()[3].len(), 68);
546        assert_eq!(parsed.padding, 7);
547        // Confirm TOC fields survive composition.
548        assert_eq!(parsed.toc.config, 15);
549        assert_eq!(parsed.toc.channels, ChannelMapping::Mono);
550        assert_eq!(parsed.toc.frame_count_code, FrameCountCode::Arbitrary);
551    }
552
553    #[test]
554    fn code3_padding_chain_255_extension() {
555        // Padding-chain spec: 255 contributes 254 padding bytes and
556        // demands another length byte. Verify that "255, 3" => 257
557        // padding bytes (254 + 3).
558        let m: u8 = 1;
559        let fc = (m << 2) | 0b10; // p=1, v=0
560        let mut packet = vec![toc(15, false, 3), fc, 255, 3];
561        // Frame body: any non-padding payload. 4 bytes is fine.
562        packet.extend_from_slice(&[0xF1, 0xF2, 0xF3, 0xF4]);
563        packet.extend(std::iter::repeat(0u8).take(257));
564        let parsed = OpusPacket::parse(&packet).unwrap();
565        assert_eq!(parsed.frame_count(), 1);
566        assert_eq!(parsed.frames()[0], &[0xF1, 0xF2, 0xF3, 0xF4]);
567        assert_eq!(parsed.padding, 257);
568    }
569
570    #[test]
571    fn code3_rejects_m_zero_per_r5() {
572        let fc = 0; // M = 0
573        let packet = [toc(15, false, 3), fc];
574        assert_eq!(OpusPacket::parse(&packet), Err(Error::MalformedPacket));
575    }
576
577    #[test]
578    fn code3_rejects_missing_frame_count_byte() {
579        // Code-3 packet with only the TOC byte is invalid (R6/R7
580        // require N >= 2).
581        let packet = [toc(15, false, 3)];
582        assert_eq!(OpusPacket::parse(&packet), Err(Error::MalformedPacket));
583    }
584
585    #[test]
586    fn code3_rejects_padding_overrunning_body() {
587        // M = 1, p=1, padding declared as 200 but only 5 bytes of
588        // payload follow.
589        let fc = (1 << 2) | 0b10;
590        let mut packet = vec![toc(15, false, 3), fc, 200];
591        packet.extend_from_slice(&[0; 5]);
592        assert_eq!(OpusPacket::parse(&packet), Err(Error::MalformedPacket));
593    }
594
595    #[test]
596    fn code3_vbr_rejects_lengths_exceeding_remaining() {
597        // M = 3, v=1, lengths 100, 100 but only 50 bytes remain.
598        let fc = (3 << 2) | 0b01;
599        let mut packet = vec![toc(15, false, 3), fc, 100, 100];
600        packet.extend_from_slice(&[0; 50]);
601        assert_eq!(OpusPacket::parse(&packet), Err(Error::MalformedPacket));
602    }
603
604    #[test]
605    fn empty_packet_rejected() {
606        assert_eq!(OpusPacket::parse(&[]), Err(Error::EmptyPacket));
607    }
608
609    // ----- Composed cases: TOC + frame packing -----
610
611    #[test]
612    fn code3_vbr_max_frame_count_48() {
613        // M = 48 (cap per R5 at 2.5 ms frames), v=1, p=0.
614        // Each frame size = 1 byte; 47 declared + 1 implicit.
615        let m: u8 = MAX_FRAMES_PER_PACKET;
616        let fc = (m << 2) | 0b01;
617        let mut packet = vec![toc(16, false, 3), fc];
618        packet.extend(std::iter::repeat(1u8).take(47)); // 47 declared lengths of 1
619        packet.extend(std::iter::repeat(0xCC).take(48));
620        let parsed = OpusPacket::parse(&packet).unwrap();
621        assert_eq!(parsed.frame_count(), 48);
622        for f in parsed.frames() {
623            assert_eq!(f, &[0xCC]);
624        }
625    }
626}