diff --git a/src/core/jitter.rs b/src/core/jitter.rs index 2e3672e..95817f3 100644 --- a/src/core/jitter.rs +++ b/src/core/jitter.rs @@ -222,5 +222,94 @@ mod tests { assert!(!jb.packets.contains_key(&0)); assert_eq!(jb.next_seq, Some(1)); } + + #[test] + fn reprimes_after_underrun_idle() { + let mut enc = OpusEncoder::new(48000, Channels::Mono, Application::Voip).unwrap(); + let mut jb = JitterBuffer::new().unwrap(); + + // Prime with seq 0, 1, 2 + jb.insert(0, frame(&mut enc, 1000)); + jb.insert(1, frame(&mut enc, 1000)); + jb.insert(2, frame(&mut enc, 1000)); + + // pop_frame() 3x -> 3 Some + assert!(jb.pop_frame().is_some()); + assert!(jb.pop_frame().is_some()); + assert!(jb.pop_frame().is_some()); + + // A 4th pop_frame() -> None, and jb.is_idle() is true + assert!(jb.pop_frame().is_none()); + assert!(jb.is_idle()); + + // Insert ONE new frame (seq 3): pop_frame() must still be None (must re-accumulate TARGET_DELAY_FRAMES) + jb.insert(3, frame(&mut enc, 1000)); + assert!(jb.pop_frame().is_none()); + assert!(!jb.is_idle()); + + // Insert seq 4 and 5 (now 3 buffered) -> pop_frame() yields Some (re-primed) + jb.insert(4, frame(&mut enc, 1000)); + jb.insert(5, frame(&mut enc, 1000)); + assert!(jb.pop_frame().is_some()); + } + + #[test] + fn duplicate_insert_does_not_grow_buffer() { + let mut jb = JitterBuffer::new().unwrap(); + jb.insert(0, vec![0u8]); + jb.insert(0, vec![1u8]); + assert_eq!(jb.packets.len(), 1); + } + + #[test] + fn is_idle_reflects_buffer_state() { + let mut enc = OpusEncoder::new(48000, Channels::Mono, Application::Voip).unwrap(); + let mut jb = JitterBuffer::new().unwrap(); + + // Fresh buffer + assert!(jb.is_idle()); + + // After a single insert + jb.insert(0, frame(&mut enc, 1000)); + assert!(!jb.is_idle()); + + // Prime (3 frames) + jb.insert(1, frame(&mut enc, 1000)); + jb.insert(2, frame(&mut enc, 1000)); + + // Drain past the end so it underruns + assert!(jb.pop_frame().is_some()); + assert!(jb.pop_frame().is_some()); + assert!(jb.pop_frame().is_some()); + assert!(jb.pop_frame().is_none()); + + assert!(jb.is_idle()); + } + + #[test] + fn overflow_resync_while_playing() { + let mut enc = OpusEncoder::new(48000, Channels::Mono, Application::Voip).unwrap(); + let mut jb = JitterBuffer::new().unwrap(); + + // Prime with seq 0, 1, 2 + jb.insert(0, frame(&mut enc, 1000)); + jb.insert(1, frame(&mut enc, 1000)); + jb.insert(2, frame(&mut enc, 1000)); + + // pop_frame() twice (now next_seq == Some(2)) + assert!(jb.pop_frame().is_some()); + assert!(jb.pop_frame().is_some()); + assert_eq!(jb.next_seq, Some(2)); + + // Insert a contiguous run of higher sequences to exceed MAX_BUFFERED_FRAMES + let start = 3; + let end = 3 + MAX_BUFFERED_FRAMES + 2; + for seq in start..end { + jb.insert(seq as u32, frame(&mut enc, 1000)); + } + + assert_eq!(jb.packets.len(), MAX_BUFFERED_FRAMES); + assert_eq!(jb.next_seq, jb.packets.keys().next().copied()); + } }