unbundle
Unbundle media files — extract still frames, audio tracks, and subtitles from video files.
unbundle provides a clean, ergonomic Rust API for extracting video frames as
image::DynamicImage
values, audio tracks as encoded byte vectors, and subtitle tracks as structured
text, powered by FFmpeg via
ffmpeg-next.
Features
- Frame extraction — by frame number, timestamp, range, interval, or specific frame list
- Audio extraction — to WAV, MP3, FLAC, or AAC (file or in-memory)
- Subtitle extraction — decode text-based subtitles to SRT, WebVTT, or raw text
- Container remuxing — lossless format conversion (e.g. MKV → MP4) without re-encoding
- Rich metadata — video dimensions, frame rate, frame count, audio sample rate, channels, codec info, multi-track audio/subtitle metadata
- Configurable output — pixel format (RGB8, RGBA8, GRAY8), target resolution with aspect ratio preservation
- Progress & cancellation — cooperative progress callbacks and
CancellationTokenfor long-running operations - Streaming iteration — lazy
FrameIterator(pull-based) andfor_each_frame(push-based) without buffering entire frame sets - Validation — inspect media files for structural issues before extraction
- Chapter support — extract chapter metadata (titles, timestamps) from containers
- Frame metadata — per-frame decode info (PTS, keyframe flag, picture type)
via
frame_with_info/frames_with_info - Segmented extraction — extract frames from multiple disjoint time ranges
in a single call with
FrameRange::Segments - Stream probing — lightweight
MediaProbefor quick metadata inspection without keeping the demuxer open - Thumbnail helpers — single-frame thumbnails, contact-sheet grids, and variance-based "smart" thumbnail selection
- Efficient seeking — seeks to the nearest keyframe, then decodes forward
- Zero-copy in-memory audio — uses FFmpeg's dynamic buffer I/O
Optional Features (feature flags)
| Feature | Description |
|---|---|
async-tokio |
FrameStream (async frame iteration) and AudioFuture via Tokio |
parallel |
frames_parallel() distributes decoding across rayon threads |
hw-accel |
Hardware-accelerated decoding (CUDA, VAAPI, DXVA2, D3D11VA, VideoToolbox, QSV) |
scene-detection |
Scene change detection via FFmpeg's scdet filter |
full |
Enables all of the above |
[]
= { = "1.1", = ["full"] }
Installation
Add unbundle to your Cargo.toml:
[]
= "1.1"
System Requirements
unbundle links against FFmpeg's native libraries via ffmpeg-next. You must
have the FFmpeg development headers and libraries installed.
Linux (Debian/Ubuntu):
macOS:
Windows:
Download FFmpeg development builds from https://ffmpeg.org/download.html or use vcpkg:
vcpkg install ffmpeg:x64-windows
Set the FFMPEG_DIR environment variable to point to your FFmpeg installation.
Quick Start
Extract Video Frames
use Duration;
use MediaUnbundler;
let mut unbundler = open?;
// Extract the first frame
let frame = unbundler.video.frame?;
frame.save?;
// Extract a frame at 30 seconds
let frame = unbundler.video.frame_at?;
frame.save?;
Extract Multiple Frames
use Duration;
use ;
let mut unbundler = open?;
// Every 30th frame
let frames = unbundler.video.frames?;
// Frames between two timestamps
let frames = unbundler.video.frames?;
// Specific frame numbers
let frames = unbundler.video.frames?;
Streaming Frame Iteration
use ;
let mut unbundler = open?;
// Push-based: process each frame without buffering
unbundler.video.for_each_frame?;
// Pull-based: lazy iterator with early exit
let iter = unbundler.video.frame_iter?;
for result in iter
Extract Audio
use Duration;
use ;
let mut unbundler = open?;
// Save complete audio track to WAV
unbundler.audio.save?;
// Extract a 30-second segment as MP3
unbundler.audio.save_range?;
// Extract audio to memory
let audio_bytes = unbundler.audio.extract?;
// Multi-track: extract the second audio track
let audio_bytes = unbundler.audio_track?.extract?;
Extract Subtitles
use ;
let mut unbundler = open?;
// Extract subtitle events with timing
let events = unbundler.subtitle.extract?;
for event in &events
// Save as SRT file
unbundler.subtitle.save?;
// Multi-track: extract the second subtitle track
unbundler.subtitle_track?.save?;
Container Remuxing
use Remuxer;
// Convert MKV to MP4 without re-encoding
new?.run?;
// Exclude subtitles during remux
new?
.exclude_subtitles
.run?;
Progress & Cancellation
use Arc;
use ;
;
let token = new;
let config = new
.with_progress
.with_cancellation;
let mut unbundler = open?;
let frames = unbundler.video.frames_with_config?;
Custom Output Format
use ;
let config = new
.with_pixel_format
.with_resolution;
let mut unbundler = open?;
let frames = unbundler.video.frames_with_config?;
Read Metadata
use MediaUnbundler;
let unbundler = open?;
let metadata = unbundler.metadata;
println!;
println!;
if let Some = &metadata.video
if let Some = &metadata.audio
// List all audio and subtitle tracks
if let Some = &metadata.audio_tracks
if let Some = &metadata.subtitle_tracks
Validate Media Files
use MediaUnbundler;
let unbundler = open?;
let report = unbundler.validate;
if report.is_valid else
Probe Media Files
use MediaProbe;
// Quick metadata inspection without keeping the file open
let metadata = probe?;
println!;
// Probe multiple files at once
let results = probe_many;
Chapter Metadata
use MediaUnbundler;
let unbundler = open?;
let metadata = unbundler.metadata;
if let Some = &metadata.chapters
Frame Metadata
use MediaUnbundler;
let mut unbundler = open?;
// Get a frame with its decode metadata
let = unbundler.video.frame_with_info?;
println!;
Thumbnail Generation
use Duration;
use ;
let mut unbundler = open?;
// Single thumbnail at a timestamp
let thumb = at_timestamp?;
// Contact-sheet grid
let config = new; // 4 columns × 3 rows
let grid = grid?;
grid.save?;
// Smart thumbnail (picks frame with highest visual variance)
let smart = smart?;
API Documentation
See the API docs for complete documentation.
Core Types
| Type | Description |
|---|---|
MediaUnbundler |
Main entry point — opens a media file and provides access to extractors |
VideoExtractor |
Extracts video frames as DynamicImage |
AudioExtractor |
Extracts audio tracks as bytes or files |
SubtitleExtractor |
Extracts text-based subtitle tracks |
Remuxer |
Lossless container format conversion |
FrameRange |
Specifies which frames to extract (range, interval, timestamps, etc.) |
FrameIterator |
Lazy, pull-based frame iterator |
AudioFormat |
Output audio format (WAV, MP3, FLAC, AAC) |
SubtitleFormat |
Output subtitle format (SRT, WebVTT, Raw) |
SubtitleEvent |
A single decoded subtitle event (text, start/end time) |
ExtractionConfig |
Threading progress callbacks, cancellation, pixel format, resolution, HW accel |
FrameOutputConfig |
Pixel format and resolution settings for frame output |
PixelFormat |
Output pixel format (RGB8, RGBA8, GRAY8) |
ValidationReport |
Result of media file validation |
MediaMetadata |
Container-level metadata (duration, format) |
VideoMetadata |
Video stream metadata (dimensions, frame rate, codec) |
AudioMetadata |
Audio stream metadata (sample rate, channels, codec) |
SubtitleMetadata |
Subtitle stream metadata (codec, language) |
ProgressCallback |
Trait for receiving progress updates |
ProgressInfo |
Progress event data (current, total, percentage, ETA) |
CancellationToken |
Cooperative cancellation via Arc<AtomicBool> |
OperationType |
Identifies the operation being tracked |
UnbundleError |
Error type with rich context |
FrameInfo |
Per-frame decode metadata (PTS, keyframe flag, picture type) |
FrameType |
Picture type enum (I, P, B, etc.) |
ChapterMetadata |
Chapter information (title, start/end times) |
MediaProbe |
Lightweight stateless media file probing |
ThumbnailGenerator |
Thumbnail generation helpers (single, grid, smart) |
ThumbnailConfig |
Grid thumbnail configuration (columns, rows, width) |
Feature-Gated Types
| Type | Feature | Description |
|---|---|---|
FrameStream |
async-tokio |
Async stream of decoded frames via Tokio |
AudioFuture |
async-tokio |
Async audio extraction future |
HwAccelMode |
hw-accel |
Hardware acceleration mode selection |
HwDeviceType |
hw-accel |
Supported HW device types (CUDA, VAAPI, etc.) |
SceneChange |
scene-detection |
Detected scene change with timestamp and score |
SceneDetectionConfig |
scene-detection |
Scene detection threshold configuration |
Examples
See the examples/ directory:
| Example | Description |
|---|---|
extract_frames |
Extract frames by number, timestamp, range, interval |
extract_audio |
Extract the complete audio track |
extract_audio_segment |
Extract a specific time range as MP3 |
thumbnail_grid |
Create a thumbnail grid from evenly-spaced frames |
metadata |
Display all media metadata |
frame_iterator |
Lazy frame iteration with early exit |
pixel_formats |
Demonstrate RGB8/RGBA8/GRAY8 output |
progress |
Progress callbacks and cancellation |
subtitles |
Extract subtitles as SRT/WebVTT/raw text |
remux |
Lossless container format conversion |
validate |
Media file validation report |
async_extraction |
Async frame streaming and audio extraction (async-tokio) |
parallel_extraction |
Parallel frame extraction across threads (parallel) |
scene_detection |
Scene change detection (scene-detection) |
hw_acceleration |
Hardware-accelerated decoding (hw-accel) |
Run an example:
Performance
- Seeking: Uses FFmpeg's keyframe-based seeking. For sequential access (ranges, intervals), frames are decoded without redundant seeks.
- Decoder lifecycle: Each extraction call creates a fresh, lightweight decoder. FFmpeg decoder creation is fast relative to actual decoding.
- Batch optimisation:
FrameRange::Specificsorts requested frame numbers and processes them in order to minimise seeks. - Streaming:
for_each_frameandFrameIteratorprocess frames one at a time without buffering the entire frame set. - Parallel extraction:
frames_parallel()(featureparallel) splits frames across rayon threads, each with its own demuxer. - Hardware acceleration: When enabled (feature
hw-accel), the decoder attempts GPU-accelerated decoding with automatic fallback to software. - Stride handling: Correctly handles FFmpeg's row padding when converting
frames to
imagebuffers. - In-memory audio: Uses
avio_open_dyn_buffor zero-copy in-memory audio encoding without temporary files.
Testing
Generate test fixtures first:
# Linux / macOS
# Windows
Then run tests:
Test Suites
| Test file | Coverage |
|---|---|
video_extraction |
Single frames, ranges, intervals, timestamps, specific lists, pixel formats, resolution scaling |
audio_extraction |
WAV/MP3/FLAC/AAC extraction, ranges, file output, multi-track |
subtitle_extraction |
Subtitle decoding, SRT/WebVTT export, multi-track |
metadata |
Container metadata, video/audio/subtitle stream properties |
config |
ExtractionConfig builder, pixel formats, resolution, cancellation |
progress |
ProgressCallback, ProgressInfo fields, CancellationToken |
error_handling |
Error variants, context, invalid inputs, missing streams |
frame_iterator |
FrameIterator, lazy iteration, early exit |
conversion |
Remuxer, stream exclusion, lossless format conversion |
validation |
ValidationReport, warnings, errors, valid files |
scene_detection |
Scene change detection, threshold configuration |
chapters |
Chapter metadata extraction, titles, timestamps, ordering |
frame_metadata |
FrameInfo, FrameType, keyframe detection, PTS values |
segmented_extraction |
FrameRange::Segments, multiple disjoint time ranges |
probing |
MediaProbe, probe/probe_many, error handling |
thumbnail |
ThumbnailGenerator, grid, smart selection, aspect ratio |
async_extraction |
FrameStream, AudioFuture, async streaming (async-tokio) |
parallel_extraction |
frames_parallel, sequential parity, interval mode (parallel) |
hw_accel |
HW device enumeration, Auto/Software modes (hw-accel) |
Benchmarks
Criterion benchmarks live in benches/:
License
MIT