#![allow(dead_code)]
use std::collections::VecDeque;
#[derive(Debug, Clone, Copy)]
pub struct StreamBufferConfig {
pub max_frames: usize,
pub sample_rate: u32,
pub channels: u16,
}
impl StreamBufferConfig {
#[must_use]
pub fn new(max_frames: usize, sample_rate: u32, channels: u16) -> Self {
Self {
max_frames,
sample_rate,
channels,
}
}
#[must_use]
pub fn max_frames(&self) -> usize {
self.max_frames
}
}
impl Default for StreamBufferConfig {
fn default() -> Self {
Self {
max_frames: 64,
sample_rate: 48_000,
channels: 2,
}
}
}
#[derive(Debug, Clone)]
pub struct StreamFrame {
pub samples: Vec<f32>,
pub pts_samples: u64,
pub channels: u16,
pub sample_rate: u32,
}
impl StreamFrame {
#[must_use]
pub fn new(samples: Vec<f32>, pts_samples: u64, channels: u16, sample_rate: u32) -> Self {
Self {
samples,
pts_samples,
channels,
sample_rate,
}
}
#[must_use]
pub fn sample_count(&self) -> usize {
if self.channels == 0 {
return 0;
}
self.samples.len() / self.channels as usize
}
#[allow(clippy::cast_precision_loss)]
#[must_use]
pub fn duration_ms(&self) -> f64 {
if self.sample_rate == 0 {
return 0.0;
}
self.sample_count() as f64 / self.sample_rate as f64 * 1_000.0
}
}
#[derive(Debug)]
pub struct StreamBuffer {
queue: VecDeque<StreamFrame>,
config: StreamBufferConfig,
total_pushed: u64,
total_popped: u64,
}
impl StreamBuffer {
#[must_use]
pub fn new(config: StreamBufferConfig) -> Self {
Self {
queue: VecDeque::with_capacity(config.max_frames),
config,
total_pushed: 0,
total_popped: 0,
}
}
pub fn push_frame(&mut self, frame: StreamFrame) -> bool {
if self.queue.len() >= self.config.max_frames {
return false;
}
self.queue.push_back(frame);
self.total_pushed += 1;
true
}
pub fn pop_frame(&mut self) -> Option<StreamFrame> {
let frame = self.queue.pop_front();
if frame.is_some() {
self.total_popped += 1;
}
frame
}
#[allow(clippy::cast_precision_loss)]
#[must_use]
pub fn duration_ms(&self) -> f64 {
self.queue.iter().map(|f| f.duration_ms()).sum()
}
#[must_use]
pub fn len(&self) -> usize {
self.queue.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.queue.is_empty()
}
#[must_use]
pub fn is_full(&self) -> bool {
self.queue.len() >= self.config.max_frames
}
#[must_use]
pub fn total_pushed(&self) -> u64 {
self.total_pushed
}
#[must_use]
pub fn total_popped(&self) -> u64 {
self.total_popped
}
}
#[cfg(test)]
mod tests {
use super::*;
fn make_frame(n_samples: usize, pts: u64) -> StreamFrame {
StreamFrame::new(
vec![0.0_f32; n_samples * 2], pts,
2,
48_000,
)
}
#[test]
fn test_config_max_frames() {
let cfg = StreamBufferConfig::new(32, 48_000, 2);
assert_eq!(cfg.max_frames(), 32);
}
#[test]
fn test_config_default() {
let cfg = StreamBufferConfig::default();
assert_eq!(cfg.max_frames, 64);
assert_eq!(cfg.sample_rate, 48_000);
}
#[test]
fn test_frame_sample_count() {
let frame = make_frame(480, 0);
assert_eq!(frame.sample_count(), 480);
}
#[test]
fn test_frame_duration_ms() {
let frame = make_frame(480, 0); let dur = frame.duration_ms();
assert!((dur - 10.0).abs() < 0.001);
}
#[test]
fn test_frame_duration_zero_rate() {
let frame = StreamFrame::new(vec![0.0; 4], 0, 2, 0);
assert_eq!(frame.duration_ms(), 0.0);
}
#[test]
fn test_buffer_push_and_pop() {
let cfg = StreamBufferConfig::default();
let mut buf = StreamBuffer::new(cfg);
let f = make_frame(480, 0);
assert!(buf.push_frame(f));
assert_eq!(buf.len(), 1);
let popped = buf.pop_frame();
assert!(popped.is_some());
assert!(buf.is_empty());
}
#[test]
fn test_buffer_fifo_order() {
let cfg = StreamBufferConfig::default();
let mut buf = StreamBuffer::new(cfg);
buf.push_frame(make_frame(480, 0));
buf.push_frame(make_frame(480, 480));
let first = buf.pop_frame().unwrap();
assert_eq!(first.pts_samples, 0);
let second = buf.pop_frame().unwrap();
assert_eq!(second.pts_samples, 480);
}
#[test]
fn test_buffer_full_rejects_push() {
let cfg = StreamBufferConfig::new(2, 48_000, 2);
let mut buf = StreamBuffer::new(cfg);
assert!(buf.push_frame(make_frame(480, 0)));
assert!(buf.push_frame(make_frame(480, 480)));
assert!(buf.is_full());
assert!(!buf.push_frame(make_frame(480, 960)));
}
#[test]
fn test_buffer_is_empty_initially() {
let buf = StreamBuffer::new(StreamBufferConfig::default());
assert!(buf.is_empty());
}
#[test]
fn test_buffer_pop_empty_returns_none() {
let mut buf = StreamBuffer::new(StreamBufferConfig::default());
assert!(buf.pop_frame().is_none());
}
#[test]
fn test_buffer_duration_ms() {
let cfg = StreamBufferConfig::default();
let mut buf = StreamBuffer::new(cfg);
buf.push_frame(make_frame(480, 0)); buf.push_frame(make_frame(480, 480)); let dur = buf.duration_ms();
assert!((dur - 20.0).abs() < 0.01);
}
#[test]
fn test_buffer_total_counters() {
let cfg = StreamBufferConfig::default();
let mut buf = StreamBuffer::new(cfg);
buf.push_frame(make_frame(480, 0));
buf.push_frame(make_frame(480, 480));
buf.pop_frame();
assert_eq!(buf.total_pushed(), 2);
assert_eq!(buf.total_popped(), 1);
}
#[test]
fn test_frame_zero_channels() {
let frame = StreamFrame::new(vec![0.0; 10], 0, 0, 48_000);
assert_eq!(frame.sample_count(), 0);
}
}