koan_core/audio/
streaming.rs1use std::io::{self, Read, Seek, SeekFrom};
9use std::sync::{Arc, Condvar, Mutex};
10
11struct Inner {
13 data: Vec<u8>,
14 total_len: Option<u64>,
16 done: bool,
18}
19
20#[derive(Clone)]
24pub struct StreamBuffer {
25 inner: Arc<(Mutex<Inner>, Condvar)>,
26}
27
28impl StreamBuffer {
29 pub fn new(total_len: Option<u64>) -> Self {
31 Self {
32 inner: Arc::new((
33 Mutex::new(Inner {
34 data: Vec::new(),
35 total_len,
36 done: false,
37 }),
38 Condvar::new(),
39 )),
40 }
41 }
42
43 pub fn push(&self, chunk: &[u8]) {
45 let (lock, cvar) = &*self.inner;
46 let mut inner = lock.lock().unwrap();
47 inner.data.extend_from_slice(chunk);
48 cvar.notify_all();
49 }
50
51 pub fn finish(&self) {
53 let (lock, cvar) = &*self.inner;
54 let mut inner = lock.lock().unwrap();
55 inner.done = true;
56 cvar.notify_all();
57 }
58
59 pub fn bytes_downloaded(&self) -> u64 {
61 let (lock, _) = &*self.inner;
62 lock.lock().unwrap().data.len() as u64
63 }
64
65 pub fn total_len(&self) -> Option<u64> {
67 let (lock, _) = &*self.inner;
68 lock.lock().unwrap().total_len
69 }
70
71 pub fn reader(&self) -> StreamingSource {
73 StreamingSource {
74 inner: self.inner.clone(),
75 pos: 0,
76 }
77 }
78}
79
80pub struct StreamingSource {
85 inner: Arc<(Mutex<Inner>, Condvar)>,
86 pos: u64,
87}
88
89impl Read for StreamingSource {
90 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
91 if buf.is_empty() {
92 return Ok(0);
93 }
94
95 let (lock, cvar) = &*self.inner;
96
97 let inner = cvar
99 .wait_while(lock.lock().unwrap(), |s| {
100 s.data.len() as u64 <= self.pos && !s.done
101 })
102 .unwrap();
103
104 let available = inner.data.len() as u64;
105 if available <= self.pos {
106 return Ok(0);
108 }
109
110 let start = self.pos as usize;
111 let end = (start + buf.len()).min(inner.data.len());
112 let n = end - start;
113 buf[..n].copy_from_slice(&inner.data[start..end]);
114 self.pos += n as u64;
115 Ok(n)
116 }
117}
118
119impl Seek for StreamingSource {
120 fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
121 let (lock, _) = &*self.inner;
122 let inner = lock.lock().unwrap();
123
124 let new_pos: i64 = match pos {
125 SeekFrom::Start(n) => n as i64,
126 SeekFrom::Current(n) => self.pos as i64 + n,
127 SeekFrom::End(n) => {
128 let len = inner.total_len.unwrap_or(inner.data.len() as u64) as i64;
130 len + n
131 }
132 };
133
134 if new_pos < 0 {
135 return Err(io::Error::new(
136 io::ErrorKind::InvalidInput,
137 "seek before beginning of stream",
138 ));
139 }
140
141 self.pos = new_pos as u64;
142 Ok(self.pos)
143 }
144}
145
146impl symphonia::core::io::MediaSource for StreamingSource {
148 fn is_seekable(&self) -> bool {
149 true
153 }
154
155 fn byte_len(&self) -> Option<u64> {
156 let (lock, _) = &*self.inner;
157 lock.lock().unwrap().total_len
158 }
159}
160
161#[cfg(test)]
162mod tests {
163 use std::io::{Read, Seek, SeekFrom};
164
165 use super::*;
166
167 fn filled_buffer(data: &[u8]) -> StreamBuffer {
168 let buf = StreamBuffer::new(Some(data.len() as u64));
169 buf.push(data);
170 buf.finish();
171 buf
172 }
173
174 #[test]
175 fn new_buffer_starts_empty() {
176 let buf = StreamBuffer::new(Some(1024));
177 assert_eq!(buf.bytes_downloaded(), 0);
178 assert_eq!(buf.total_len(), Some(1024));
179 }
180
181 #[test]
182 fn new_buffer_unknown_total() {
183 let buf = StreamBuffer::new(None);
184 assert_eq!(buf.total_len(), None);
185 }
186
187 #[test]
188 fn read_all_data_available() {
189 let data = b"hello streaming world";
190 let buf = filled_buffer(data);
191 let mut src = buf.reader();
192
193 let mut out = Vec::new();
194 src.read_to_end(&mut out).unwrap();
195 assert_eq!(out, data);
196 }
197
198 #[test]
199 fn read_partial_then_rest() {
200 let data = b"abcdefghij";
201 let buf = filled_buffer(data);
202 let mut src = buf.reader();
203
204 let mut first = [0u8; 4];
205 let n = src.read(&mut first).unwrap();
206 assert_eq!(n, 4);
207 assert_eq!(&first, b"abcd");
208
209 let mut rest = Vec::new();
210 src.read_to_end(&mut rest).unwrap();
211 assert_eq!(rest, b"efghij");
212 }
213
214 #[test]
215 fn seek_from_start() {
216 let data = b"0123456789";
217 let buf = filled_buffer(data);
218 let mut src = buf.reader();
219
220 let pos = src.seek(SeekFrom::Start(5)).unwrap();
221 assert_eq!(pos, 5);
222
223 let mut out = [0u8; 3];
224 src.read_exact(&mut out).unwrap();
225 assert_eq!(&out, b"567");
226 }
227
228 #[test]
229 fn seek_from_current() {
230 let data = b"0123456789";
231 let buf = filled_buffer(data);
232 let mut src = buf.reader();
233
234 src.seek(SeekFrom::Start(2)).unwrap();
235 let pos = src.seek(SeekFrom::Current(3)).unwrap();
236 assert_eq!(pos, 5);
237
238 let mut out = [0u8; 2];
239 src.read_exact(&mut out).unwrap();
240 assert_eq!(&out, b"56");
241 }
242
243 #[test]
244 fn seek_from_end() {
245 let data = b"0123456789";
246 let buf = filled_buffer(data);
247 let mut src = buf.reader();
248
249 let pos = src.seek(SeekFrom::End(0)).unwrap();
251 assert_eq!(pos, 10);
252
253 let pos = src.seek(SeekFrom::End(-3)).unwrap();
255 assert_eq!(pos, 7);
256
257 let mut out = [0u8; 3];
258 src.read_exact(&mut out).unwrap();
259 assert_eq!(&out, b"789");
260 }
261
262 #[test]
263 fn seek_before_start_errors() {
264 let data = b"hello";
265 let buf = filled_buffer(data);
266 let mut src = buf.reader();
267
268 let result = src.seek(SeekFrom::Current(-1));
269 assert!(result.is_err());
270 }
271
272 #[test]
273 fn is_complete_when_done() {
274 let buf = StreamBuffer::new(Some(5));
275 buf.push(b"hello");
276 assert!(buf.bytes_downloaded() != 0); buf.finish();
279 let mut src = buf.reader();
281 let mut out = Vec::new();
282 src.read_to_end(&mut out).unwrap();
283 assert_eq!(out, b"hello");
284 }
285
286 #[test]
287 fn byte_len_returns_total() {
288 use symphonia::core::io::MediaSource;
289 let buf = StreamBuffer::new(Some(42));
290 let src = buf.reader();
291 assert_eq!(src.byte_len(), Some(42));
292 }
293
294 #[test]
295 fn is_seekable_true() {
296 use symphonia::core::io::MediaSource;
297 let buf = StreamBuffer::new(None);
298 let src = buf.reader();
299 assert!(src.is_seekable());
300 }
301
302 #[test]
303 fn push_increments_bytes_downloaded() {
304 let buf = StreamBuffer::new(Some(10));
305 buf.push(b"hello");
306 assert_eq!(buf.bytes_downloaded(), 5);
307 buf.push(b"world");
308 assert_eq!(buf.bytes_downloaded(), 10);
309 }
310
311 #[test]
312 fn multiple_readers_independent_positions() {
313 let data = b"0123456789";
314 let buf = filled_buffer(data);
315
316 let mut r1 = buf.reader();
317 let mut r2 = buf.reader();
318
319 r1.seek(SeekFrom::Start(7)).unwrap();
320
321 let mut out1 = [0u8; 3];
322 r1.read_exact(&mut out1).unwrap();
323 assert_eq!(&out1, b"789");
324
325 let mut out2 = [0u8; 3];
326 r2.read_exact(&mut out2).unwrap();
327 assert_eq!(&out2, b"012");
328 }
329
330 #[test]
331 fn test_partial_availability() {
332 let data: Vec<u8> = (0u8..=255).cycle().take(1000).collect();
334 let buf = StreamBuffer::new(Some(1000));
335 buf.push(&data[..500]);
336
337 assert_eq!(buf.bytes_downloaded(), 500);
338 assert_eq!(buf.total_len(), Some(1000));
339
340 let buf2 = buf.clone();
342 std::thread::spawn(move || {
343 std::thread::sleep(std::time::Duration::from_millis(10));
344 buf2.finish();
345 });
346
347 let mut src = buf.reader();
348 let mut out = Vec::new();
349 src.read_to_end(&mut out).unwrap();
350 assert_eq!(out.len(), 500);
351 assert_eq!(out, &data[..500]);
352 }
353}