Understanding MediaSource, MediaSourceStream, and how Symphonia handles I/O
Symphonia provides a sophisticated I/O layer that abstracts over different input sources while optimizing for performance. Understanding this layer is key to efficiently reading audio from files, network streams, or custom sources.
The MediaSource trait (symphonia-core/src/io/mod.rs:42) is the foundation of Symphonia’s I/O system.
pub trait MediaSource: io::Read + io::Seek + Send + Sync { /// Returns if the source is seekable fn is_seekable(&self) -> bool; /// Returns the length in bytes, if available fn byte_len(&self) -> Option<u64>;}
Despite requiring io::Seek, seeking is optional. The is_seekable() method indicates at runtime whether seeking is supported.
use std::fs::File;use symphonia::core::io::MediaSourceStream;// Open filelet file = File::open("audio.mp3")?;// Wrap in MediaSourceStreamlet mss = MediaSourceStream::new(Box::new(file), Default::default());
MediaSourceStream (symphonia-core/src/io/media_source_stream.rs:52) is Symphonia’s supercharged buffered reader. It wraps any MediaSource and provides:
Regular seek() calls (via io::Seek) invalidate the buffer. Use seek_buffered() for efficient local seeking.
When a read spans the ring buffer boundary:
Ring Buffer:[####________########] ^ ^ write read
MediaSourceStream uses vectored I/O to read into both contiguous regions in one system call:
let ring_vectors = &mut [ IoSliceMut::new(vec0), // From write pos to end IoSliceMut::new(vec1), // From start to needed length];self.inner.read_vectored(ring_vectors)?;
The seekback buffer allows efficient backward seeking:
// Ensure you can seek back 16KBmss.ensure_seekback_buffer(16 * 1024);// Now you can efficiently seek backwardmss.seek_buffered_rev(8 * 1024); // No I/O needed
Useful for:
Format probing that needs to retry
Decoders that need lookahead/lookbehind
Error recovery with retries
Avoid Excessive Seeking
Each seek() call via io::Seek invalidates the buffer:
// Bad - buffer invalidated each timefor offset in offsets { mss.seek(SeekFrom::Start(offset))?; let byte = mss.read_byte()?;}// Better - use buffered seeking if possiblefor offset in offsets { mss.seek_buffered(offset); let byte = mss.read_byte()?;}
Sequential Reading is Fastest
The exponential read-ahead buffer optimizes for sequential access:
// Optimal - sequential reading grows buffer to 32KBloop { let packet = format.next_packet()?; // Process...}// Suboptimal - seeking resets buffer to 1KBfor ts in timestamps { format.seek(SeekMode::Accurate, SeekTo::Time { time, track_id })?; let packet = format.next_packet()?;}