💭 Why media-seek?
Downloading only a time clip from a streaming URL requires knowing which bytes to request. Every container format stores this information differently — MP4 uses a sidx box, WebM has EBML Cues, MP3 uses the Xing TOC, and so on.
media-seek abstracts all of that. You feed it the leading bytes of a stream and it returns a ContainerIndex that answers one question: given a time window [start, end], which bytes do I need to fetch?
There is no HTTP client bundled and no subprocess spawned. Callers implement the two-line RangeFetcher trait to supply bytes from whatever transport they use. Formats whose index lies outside the probe window (WebM Cues, OGG page bisection, AVI idx1, MPEG-TS PCR search) request additional ranges through that trait automatically.
📥 How to get it
Add the following to your Cargo.toml file:
[]
= "0.2.0"
Check the releases page for the latest version.
🔍 Observability & Tracing
This crate always includes the tracing crate. It emits debug events for each format detection, parse step, and extra fetch.
⚠️ Important: tracing macros are pure no-ops without a configured subscriber. If you don't add one, there is zero runtime overhead.
To capture logs, add a subscriber in your application:
[]
= "0.3"
use Level;
use FmtSubscriber;
let subscriber = builder
.with_max_level
.finish;
set_global_default
.expect;
Refer to the tracing-subscriber documentation for more advanced configuration (JSON output, log levels, targets, etc.).
🎯 Supported formats
| Extension | Index type | Precision | Extra fetches |
|---|---|---|---|
mp4, m4a |
fMP4 SIDX box | Fragment boundary | No |
webm |
EBML Cues element | Cluster boundary | Maybe |
mp3 |
Xing/VBRI TOC or CBR avg | Frame / 1 % TOC entry | No |
ogg |
OGG page granule bisection | Page boundary | Yes (up to 64) |
flac |
SEEKTABLE metadata block | Seek point | No |
wav |
PCM formula | BlockAlign-exact | No |
aiff |
PCM formula | Sample-exact | No |
aac |
ADTS frame scan average | ~21 ms frame | No |
flv |
AMF0 onMetaData keyframes | Keyframe | No |
avi |
idx1 chunk at EOF |
Frame | Yes (1 fetch) |
ts |
PCR binary search | ~11 ms TS packet | Yes (up to 64) |
MHTML (storyboard segments), None, and unrecognized magic bytes return Err(Error::UnsupportedFormat).
🚀 Quick start
1. Implement RangeFetcher
use RangeFetcher;
2. Parse the container index
use ;
# ;
#
# async
3. Translate timestamps to byte ranges
# use ;
# async
📖 Documentation
The full API reference is available on docs.rs.
parse()
pub async ;
Detects the container format from magic bytes in probe and dispatches to the appropriate parser. Returns Err(Error::UnsupportedFormat) for unrecognised formats.
ContainerIndex
RangeFetcher trait
fetch is called only when extra data is required beyond the initial probe:
- WebM — when the Cues element starts beyond the probe window.
- OGG — up to 64 equidistant binary-search probes across the stream.
- AVI — one fetch of the last 64 KB to locate the
idx1chunk. - MPEG-TS — up to 64 equidistant binary-search probes for PCR timestamps.
All other formats (MP4, MP3, FLAC, WAV, AIFF, AAC, FLV) parse entirely from the probe.
🚨 Error handling
UnsupportedFormat is the expected case for storyboard MHTML segments and non-media content. Callers should handle it by falling back to a full download or reporting that seeking is unavailable.
🤝 Contributing
Contributions are welcome! Please feel free to submit a Pull Request. Make sure to follow the Contributing Guidelines.
📄 License
This project is licensed under the GPL-3.0 License.