Skip to main content
MediaSource is a composite trait combining std::io::Read and std::io::Seek that represents a source of media data for Symphonia.

Overview

MediaSource provides a unified interface for reading media from files, network streams, or any other data source. Despite requiring Seek, seeking is an optional capability that can be queried at runtime.

Trait Definition

pub trait MediaSource: io::Read + io::Seek + Send + Sync {
    fn is_seekable(&self) -> bool;
    fn byte_len(&self) -> Option<u64>;
}

Required Methods

is_seekable

fn is_seekable(&self) -> bool
Returns whether the source supports seeking. This may be an expensive operation.
For file-based sources, this queries the underlying file descriptor metadata and may involve system calls. Cache the result if used frequently.
Example:
use symphonia::core::io::MediaSource;
use std::fs::File;

let file = File::open("audio.mp3")?;
if file.is_seekable() {
    println!("Can seek in this file");
}

byte_len

fn byte_len(&self) -> Option<u64>
Returns the length of the source in bytes, if available. Returns None for streams of unknown length. Example:
if let Some(len) = source.byte_len() {
    println!("Source is {} bytes", len);
}

Standard Implementations

File

MediaSource is implemented for std::fs::File.
use std::fs::File;
use symphonia::core::io::MediaSourceStream;

let file = File::open("audio.flac")?;
let mss = MediaSourceStream::new(Box::new(file), Default::default());
Implementation Details:
  • is_seekable(): Returns true if the file is a regular file (not a FIFO, device, etc.)
  • byte_len(): Returns the file size from metadata

Cursor

MediaSource is implemented for std::io::Cursor<T> where T: AsRef<[u8]>.
use std::io::Cursor;
use symphonia::core::io::MediaSourceStream;

let data: Vec<u8> = load_audio_data();
let cursor = Cursor::new(data);
let mss = MediaSourceStream::new(Box::new(cursor), Default::default());
Implementation Details:
  • is_seekable(): Always returns true
  • byte_len(): Returns the length of the underlying buffer

ReadOnlySource

For sources that only implement std::io::Read, use ReadOnlySource to wrap them in an unseekable MediaSource.
pub struct ReadOnlySource<R: io::Read>

Creating ReadOnlySource

new

pub fn new(inner: R) -> Self
Wraps a reader in a ReadOnlySource.
inner
R: io::Read
required
The reader to wrap
Example:
use symphonia::core::io::{ReadOnlySource, MediaSourceStream};
use std::io;

// Wrap stdin as a media source
let stdin_source = ReadOnlySource::new(io::stdin());
let mss = MediaSourceStream::new(Box::new(stdin_source), Default::default());

ReadOnlySource Methods

get_ref

pub fn get_ref(&self) -> &R
Gets an immutable reference to the underlying reader.

get_mut

pub fn get_mut(&mut self) -> &mut R
Gets a mutable reference to the underlying reader.

into_inner

pub fn into_inner(self) -> R
Unwraps the ReadOnlySource, returning the underlying reader.

Usage Patterns

File-Based Media

use std::fs::File;
use symphonia::core::io::MediaSourceStream;
use symphonia::core::probe::Hint;

fn open_media_file(path: &str) -> Result<MediaSourceStream, Box<dyn std::error::Error>> {
    let file = File::open(path)?;
    let mss = MediaSourceStream::new(Box::new(file), Default::default());
    Ok(mss)
}

Network Streaming

use symphonia::core::io::{ReadOnlySource, MediaSourceStream};
use std::io::Read;

fn stream_from_network<R: Read + Send + Sync + 'static>(
    stream: R
) -> MediaSourceStream {
    let source = ReadOnlySource::new(stream);
    MediaSourceStream::new(Box::new(source), Default::default())
}

Standard Input

use symphonia::core::io::{ReadOnlySource, MediaSourceStream};
use std::io;

fn read_from_stdin() -> MediaSourceStream {
    let stdin = io::stdin();
    let source = ReadOnlySource::new(stdin);
    MediaSourceStream::new(Box::new(source), Default::default())
}

In-Memory Buffer

use std::io::Cursor;
use symphonia::core::io::MediaSourceStream;

fn from_memory(data: Vec<u8>) -> MediaSourceStream {
    let cursor = Cursor::new(data);
    MediaSourceStream::new(Box::new(cursor), Default::default())
}

Type Erasure Pattern

MediaSource uses type erasure to allow different source types to be used interchangeably:
use symphonia::core::io::MediaSourceStream;
use std::io::Cursor;
use std::fs::File;

fn create_source(use_file: bool) -> Box<dyn MediaSource> {
    if use_file {
        Box::new(File::open("audio.mp3").unwrap())
    } else {
        let data = vec![0u8; 1024];
        Box::new(Cursor::new(data))
    }
}

let source = create_source(true);
let mss = MediaSourceStream::new(source, Default::default());

Implementing MediaSource

To implement MediaSource for a custom type, implement Read, Seek, and the MediaSource methods:
use symphonia::core::io::MediaSource;
use std::io::{self, Read, Seek, SeekFrom};

struct CustomSource {
    data: Vec<u8>,
    pos: usize,
}

impl Read for CustomSource {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        let remaining = &self.data[self.pos..];
        let to_read = buf.len().min(remaining.len());
        buf[..to_read].copy_from_slice(&remaining[..to_read]);
        self.pos += to_read;
        Ok(to_read)
    }
}

impl Seek for CustomSource {
    fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
        let new_pos = match pos {
            SeekFrom::Start(n) => n as i64,
            SeekFrom::Current(n) => self.pos as i64 + n,
            SeekFrom::End(n) => self.data.len() as i64 + n,
        };
        
        if new_pos < 0 || new_pos > self.data.len() as i64 {
            return Err(io::Error::new(io::ErrorKind::InvalidInput, "invalid seek"));
        }
        
        self.pos = new_pos as usize;
        Ok(self.pos as u64)
    }
}

impl MediaSource for CustomSource {
    fn is_seekable(&self) -> bool {
        true
    }
    
    fn byte_len(&self) -> Option<u64> {
        Some(self.data.len() as u64)
    }
}

Seeking Behavior

Seekable Sources

When a source is seekable, format readers can:
  • Skip to specific positions for initialization
  • Implement efficient seeking to timestamps
  • Read metadata from specific file locations
let file = File::open("audio.mp3")?;
if file.is_seekable() {
    // Format reader can perform optimized seeking
}

Non-Seekable Sources

When a source is not seekable:
  • Format readers must process data sequentially
  • Seeking to timestamps may not be available
  • Some formats may not be detectable
use symphonia::core::io::ReadOnlySource;

let stream = get_network_stream();
let source = ReadOnlySource::new(stream);
// source.is_seekable() returns false

Performance Considerations

Both is_seekable() and byte_len() may involve system calls for file-based sources. Cache these values when possible:
struct CachedMediaInfo {
    is_seekable: bool,
    byte_len: Option<u64>,
}

fn get_media_info(source: &dyn MediaSource) -> CachedMediaInfo {
    CachedMediaInfo {
        is_seekable: source.is_seekable(),
        byte_len: source.byte_len(),
    }
}

Complete Example

use symphonia::core::io::{MediaSource, MediaSourceStream, ReadOnlySource};
use symphonia::core::probe::Hint;
use std::fs::File;
use std::io;

fn open_audio_source(path: Option<&str>) -> Result<MediaSourceStream, Box<dyn std::error::Error>> {
    let source: Box<dyn MediaSource> = if let Some(path) = path {
        // Open file
        Box::new(File::open(path)?)
    } else {
        // Use stdin
        Box::new(ReadOnlySource::new(io::stdin()))
    };
    
    // Check source capabilities
    if source.is_seekable() {
        if let Some(len) = source.byte_len() {
            println!("Opened seekable source of {} bytes", len);
        }
    } else {
        println!("Opened non-seekable stream");
    }
    
    // Create media source stream
    let mss = MediaSourceStream::new(source, Default::default());
    
    Ok(mss)
}

See Also

Build docs developers (and LLMs) love