use std::io; use std::{collections::VecDeque, pin::Pin, sync::Arc, task::{Context, Poll}}; use tokio::{io::{AsyncRead, ReadBuf}, sync::{RwLock, mpsc}}; /// PCM chunk with audio data and timestamp for precise pacing. #[derive(Debug)] pub struct PcmChunk { /// Raw PCM audio bytes pub bytes: Vec, /// Timestamp in seconds (from AudioSegment) pub timestamp_sec: f64, /// Duration in seconds of this PCM chunk (samples / sample_rate) pub duration_sec: f64, } /// AsyncRead adapter for mpsc::Receiver. /// Extracts bytes from PcmChunk and provides them to the FLAC encoder. pub struct ByteStreamReader { rx: mpsc::Receiver, buffer: VecDeque, finished: bool, /// Shared timestamp for broadcaster pacing current_timestamp: Arc>, /// Shared duration for broadcaster pacing current_duration: Arc>, } impl ByteStreamReader { pub fn new( rx: mpsc::Receiver, current_timestamp: Arc>, current_duration: Arc>, ) -> Self { Self { rx, buffer: VecDeque::new(), finished: false, current_timestamp, current_duration, } } } impl AsyncRead for ByteStreamReader { fn poll_read( mut self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut ReadBuf<'_>, ) -> Poll> { loop { if !self.buffer.is_empty() { let to_copy = self.buffer.len().min(buf.remaining()); if to_copy == 0 { return Poll::Ready(Ok(())); } let slice = self.buffer.make_contiguous(); buf.put_slice(&slice[..to_copy]); self.buffer.drain(..to_copy); return Poll::Ready(Ok(())); } if self.finished { return Poll::Ready(Ok(())); } match Pin::new(&mut self.rx).poll_recv(cx) { Poll::Ready(Some(chunk)) => { if chunk.bytes.is_empty() { continue; } // Update shared timestamp and duration for broadcaster pacing if let Ok(mut ts) = self.current_timestamp.try_write() { *ts = chunk.timestamp_sec; } if let Ok(mut dur) = self.current_duration.try_write() { *dur = chunk.duration_sec; } self.buffer.extend(chunk.bytes); } Poll::Ready(None) => { self.finished = true; return Poll::Ready(Ok(())); } Poll::Pending => return Poll::Pending, } } } }