unbundle 3.0.0

Unbundle media files - extract still frames, audio tracks, and subtitles from video files
Documentation

unbundle

Crates.io docs.rs License: MIT

A clean, ergonomic Rust library for extracting video frames, audio tracks, and subtitles from media files using FFmpeg.

use unbundle::MediaUnbundler;

let mut unbundler = MediaUnbundler::open("video.mp4")?;

// Extract a frame at 30 seconds
let frame = unbundler.video().frame_at(Duration::from_secs(30))?;
frame.save("frame_30s.png")?;

// Extract complete audio track
unbundler.audio().save("audio.wav", AudioFormat::Wav)?;

Why unbundle?

  • Type-safe API — frames as image::DynamicImage, audio as bytes or files, subtitles as structured events
  • Flexible extraction — by frame number, timestamp, range, interval, or custom frame lists
  • Streaming support — lazy iterators and async streams avoid buffering entire frame sets
  • Rich metadata — dimensions, frame rates, codecs, chapters, per-frame decode info
  • Production-ready — progress callbacks, cancellation tokens, hardware acceleration, parallel processing

Use Cases

  • Video thumbnails — contact sheets, smart frame selection, chapter previews
  • Media processing — format conversion, audio extraction, subtitle manipulation
  • Analysis tools — scene detection, keyframe analysis, GOP structure inspection
  • Content indexing — frame extraction for search, waveform visualization
  • Transcoding pipelines — lossless remuxing, audio re-encoding

Installation

Add to your Cargo.toml:

[dependencies]
unbundle = "3.0"

Or with additional features:

[dependencies]
unbundle = { version = "3.0", features = ["async-tokio", "parallel", "hw-accel"] }

System Requirements

unbundle requires FFmpeg libraries (4.0+) installed on your system.

Linux (Debian/Ubuntu):

sudo apt-get install libavcodec-dev libavformat-dev libavutil-dev \
    libswscale-dev libswresample-dev libavdevice-dev pkg-config

macOS:

brew install ffmpeg pkg-config

Windows:

Download FFmpeg development builds from https://ffmpeg.org/download.html or use vcpkg:

vcpkg install ffmpeg:x64-windows

Set FFMPEG_DIR environment variable if FFmpeg is not in your PATH.

Quick Start

Extract Video Frames

use std::time::Duration;
use unbundle::MediaUnbundler;

let mut unbundler = MediaUnbundler::open("input.mp4")?;

// Extract the first frame
let frame = unbundler.video().frame(0)?;
frame.save("first_frame.png")?;

// Extract a frame at 30 seconds
let frame = unbundler.video().frame_at(Duration::from_secs(30))?;
frame.save("frame_30s.png")?;

Extract Multiple Frames

use std::time::Duration;
use unbundle::{FrameRange, MediaUnbundler};

let mut unbundler = MediaUnbundler::open("input.mp4")?;

// Every 30th frame
let frames = unbundler.video().frames(FrameRange::Interval(30))?;

// Frames between two timestamps
let frames = unbundler.video().frames(
    FrameRange::TimeRange(Duration::from_secs(10), Duration::from_secs(20))
)?;

// Specific frame numbers
let frames = unbundler.video().frames(
    FrameRange::Specific(vec![0, 50, 100, 150])
)?;

Streaming Frame Iteration

use unbundle::{FrameRange, MediaUnbundler};

let mut unbundler = MediaUnbundler::open("input.mp4")?;

// Push-based: process each frame without buffering
unbundler.video().for_each_frame(
    FrameRange::Range(0, 99),
    |frame_number, image| {
        image.save(format!("frame_{frame_number}.png"))?;
        Ok(())
    },
)?;

// Pull-based: lazy iterator with early exit
let iter = unbundler.video().frame_iter(FrameRange::Range(0, 99))?;
for result in iter {
    let (frame_number, image) = result?;
    image.save(format!("frame_{frame_number}.png"))?;
}

Extract Audio

use std::time::Duration;
use unbundle::{AudioFormat, MediaUnbundler};

let mut unbundler = MediaUnbundler::open("input.mp4")?;

// Save complete audio track to WAV
unbundler.audio().save("output.wav", AudioFormat::Wav)?;

// Extract a 30-second segment as MP3
unbundler.audio().save_range(
    "segment.mp3",
    Duration::from_secs(30),
    Duration::from_secs(60),
    AudioFormat::Mp3,
)?;

// Extract audio to memory
let audio_bytes = unbundler.audio().extract(AudioFormat::Wav)?;

// Multi-track: extract the second audio track
let audio_bytes = unbundler.audio_track(1)?.extract(AudioFormat::Wav)?;

Extract Subtitles

use unbundle::{MediaUnbundler, SubtitleFormat};

let mut unbundler = MediaUnbundler::open("input.mkv")?;

// Extract subtitle events with timing
let events = unbundler.subtitle().extract()?;
for event in &events {
    println!("[{:?}{:?}] {}", event.start_time, event.end_time, event.text);
}

// Save as SRT file
unbundler.subtitle().save("output.srt", SubtitleFormat::Srt)?;

// Multi-track: extract the second subtitle track
unbundler.subtitle_track(1)?.save("track2.vtt", SubtitleFormat::WebVtt)?;

Progress & Cancellation

use std::sync::Arc;
use unbundle::{
    CancellationToken, ExtractionConfig, FrameRange,
    MediaUnbundler, ProgressCallback, ProgressInfo,
};

struct PrintProgress;
impl ProgressCallback for PrintProgress {
    fn on_progress(&self, info: &ProgressInfo) {
        println!("Frame {}/{}", info.current, info.total.unwrap_or(0));
    }
}

let token = CancellationToken::new();
let config = ExtractionConfig::new()
    .with_progress(Arc::new(PrintProgress))
    .with_cancellation(token.clone());

let mut unbundler = MediaUnbundler::open("input.mp4")?;
let frames = unbundler.video().frames_with_config(
    FrameRange::Range(0, 99),
    &config,
)?;

Features

Core Capabilities

  • 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 CancellationToken for long-running operations
  • Streaming iteration — lazy FrameIterator (pull-based) and for_each_frame (push-based) without buffering entire frame sets
  • Audio sample iteration — lazy AudioIterator yields mono f32 chunks for incremental audio processing
  • 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 MediaProbe for quick metadata inspection without keeping the demuxer open
  • Thumbnail helpers — single-frame thumbnails, contact-sheet grids, and variance-based "smart" thumbnail selection
  • Keyframe & GOP analysis — scan video packets for keyframe positions and GOP structure without decoding
  • VFR detection — detect variable frame rate streams and analyze PTS distributions
  • Packet iteration — raw packet-level demuxer iteration for advanced inspection
  • Efficient seeking — seeks to the nearest keyframe, then decodes forward
  • Zero-copy in-memory audio — uses FFmpeg's dynamic buffer I/O

Optional Features

Enable additional functionality through Cargo features:

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
gif Animated GIF export from video frames
waveform Audio waveform visualization data (min/max/RMS per bin)
loudness Peak/RMS loudness analysis with dBFS conversion
transcode Audio re-encoding between formats (e.g. AAC → MP3)
video-writer Encode DynamicImage sequences into video files (H.264, H.265, MPEG-4)
full Enables all of the above
[dependencies]
unbundle = { version = "3.0", features = ["full"] }

Feature Usage Guide

  • Use async-tokio when integrating with async web servers or when processing multiple videos concurrently
  • Use parallel for CPU-intensive batch frame extraction (e.g., generating thousands of thumbnails)
  • Use hw-accel when processing high-resolution video (4K+) or when CPU is a bottleneck
  • Use scene-detection for video analysis, automatic chapter detection, or intelligent thumbnail selection
  • Use gif for creating preview animations or social media content
  • Use waveform and loudness** for audio visualization or normalization workflows
  • Use transcode for audio format conversion in media pipelines
  • Use video-writer for creating time-lapses, slideshows, or re-encoding frame sequences

Examples

The examples/ directory contains complete, runnable examples:

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)
gif_export Export video frames as animated GIF (gif)
waveform_analysis Generate audio waveform data (waveform)
loudness_analysis Analyze audio loudness levels (loudness)
audio_iterator Lazy audio sample iteration
write_video Encode image sequences into video files (video-writer)
transcode Re-encode audio between formats (transcode)
keyframe_analysis GOP/keyframe structure analysis
vfr_detection Variable frame rate detection
packet_inspect Raw packet-level demuxer inspection
subtitle_search Search subtitle text content

Run an example:

cargo run --example metadata -- path/to/video.mp4

Advanced Usage

Container Remuxing

use unbundle::Remuxer;

// Convert MKV to MP4 without re-encoding
Remuxer::new("input.mkv", "output.mp4")?.run()?;

// Exclude subtitles during remux
Remuxer::new("input.mkv", "output.mp4")?
    .exclude_subtitles()
    .run()?;

Custom Output Format

use unbundle::{ExtractionConfig, FrameRange, MediaUnbundler, PixelFormat};

let config = ExtractionConfig::new()
    .with_pixel_format(PixelFormat::Rgba8)
    .with_resolution(Some(1280), Some(720));

let mut unbundler = MediaUnbundler::open("input.mp4")?;
let frames = unbundler.video().frames_with_config(
    FrameRange::Interval(30),
    &config,
)?;

Read Metadata

use unbundle::MediaUnbundler;

let unbundler = MediaUnbundler::open("input.mp4")?;
let metadata = unbundler.metadata();

println!("Duration: {:?}", metadata.duration);
println!("Format: {}", metadata.format);

if let Some(video) = &metadata.video {
    println!("Video: {}x{}, {:.2} fps, {} frames",
        video.width, video.height,
        video.frames_per_second, video.frame_count);
    println!("Codec: {}", video.codec);
}

if let Some(audio) = &metadata.audio {
    println!("Audio: {} Hz, {} channels, codec: {}",
        audio.sample_rate, audio.channels, audio.codec);
}

// List all audio and subtitle tracks
if let Some(tracks) = &metadata.audio_tracks {
    println!("{} audio track(s)", tracks.len());
}
if let Some(tracks) = &metadata.subtitle_tracks {
    println!("{} subtitle track(s)", tracks.len());
}

Validate Media Files

use unbundle::MediaUnbundler;

let unbundler = MediaUnbundler::open("input.mp4")?;
let report = unbundler.validate();

if report.is_valid() {
    println!("File is valid");
} else {
    println!("{report}");
}

Probe Media Files

use unbundle::MediaProbe;

// Quick metadata inspection without keeping the file open
let metadata = MediaProbe::probe("input.mp4")?;
println!("Duration: {:?}", metadata.duration);

// Probe multiple files at once
let results = MediaProbe::probe_many(&["video1.mp4", "video2.mkv"]);

Chapter Metadata

use unbundle::MediaUnbundler;

let unbundler = MediaUnbundler::open("input.mkv")?;
let metadata = unbundler.metadata();

if let Some(chapters) = &metadata.chapters {
    for chapter in chapters {
        println!("[{:?}{:?}] {}",
            chapter.start, chapter.end,
            chapter.title.as_deref().unwrap_or("Untitled"));
    }
}

Frame Metadata

use unbundle::MediaUnbundler;

let mut unbundler = MediaUnbundler::open("input.mp4")?;

// Get a frame with its decode metadata
let (image, info) = unbundler.video().frame_with_info(0)?;
println!("Frame {}: keyframe={}, type={:?}, pts={:?}",
    info.frame_number, info.is_keyframe, info.frame_type, info.pts);

Thumbnail Generation

use std::time::Duration;
use unbundle::{MediaUnbundler, ThumbnailConfig, ThumbnailGenerator};

let mut unbundler = MediaUnbundler::open("input.mp4")?;

// Single thumbnail at a timestamp
let thumb = ThumbnailGenerator::at_timestamp(&mut unbundler, Duration::from_secs(5), 320)?;

// Contact-sheet grid
let config = ThumbnailConfig::new(4, 3); // 4 columns × 3 rows
let grid = ThumbnailGenerator::grid(&mut unbundler, &config)?;
grid.save("contact_sheet.png")?;

// Smart thumbnail (picks frame with highest visual variance)
let smart = ThumbnailGenerator::smart(&mut unbundler, 10, 320)?;

GIF Export

use std::time::Duration;
use unbundle::{FrameRange, GifConfig, MediaUnbundler};

let mut unbundler = MediaUnbundler::open("input.mp4")?;

let config = GifConfig::new().width(320).frame_delay(10);
unbundler.video().export_gif(
    "output.gif",
    FrameRange::TimeRange(Duration::from_secs(0), Duration::from_secs(5)),
    &config,
)?;

// Or export to memory
let bytes = unbundler.video().export_gif_to_memory(
    FrameRange::Interval(10),
    &config,
)?;

Audio Waveform

use unbundle::{MediaUnbundler, WaveformConfig};

let mut unbundler = MediaUnbundler::open("input.mp4")?;
let waveform = unbundler.audio().generate_waveform(
    &WaveformConfig::new().bins(1000),
)?;

for bin in &waveform.bins {
    println!("min={:.3} max={:.3} rms={:.3}", bin.min, bin.max, bin.rms);
}

Loudness Analysis

use unbundle::MediaUnbundler;

let mut unbundler = MediaUnbundler::open("input.mp4")?;
let loudness = unbundler.audio().analyze_loudness()?;
println!("Peak: {:.1} dBFS, RMS: {:.1} dBFS", loudness.peak_dbfs, loudness.rms_dbfs);

Audio Sample Iteration

use unbundle::MediaUnbundler;

let mut unbundler = MediaUnbundler::open("input.mp4")?;
let iter = unbundler.audio().sample_iter()?;
let mut total_samples = 0u64;
for chunk in iter {
    let chunk = chunk?;
    total_samples += chunk.samples.len() as u64;
}
println!("Total mono samples: {total_samples}");

Audio Transcoding

use unbundle::{AudioFormat, MediaUnbundler, Transcoder};

let mut unbundler = MediaUnbundler::open("input.mp4")?;

// Re-encode audio from the source format to MP3
Transcoder::new(&mut unbundler)
    .format(AudioFormat::Mp3)
    .run("output.mp3")?;

Video Writing

use unbundle::{MediaUnbundler, FrameRange, VideoWriter, VideoWriterConfig, VideoCodec};

let mut unbundler = MediaUnbundler::open("input.mp4")?;
let frames = unbundler.video().frames(FrameRange::Interval(30))?;

let config = VideoWriterConfig::new(1920, 1080)
    .fps(24)
    .codec(VideoCodec::H264);
VideoWriter::new(config).write("output.mp4", &frames)?;

Keyframe & GOP Analysis

use unbundle::MediaUnbundler;

let mut unbundler = MediaUnbundler::open("input.mp4")?;
let gop = unbundler.video().analyze_gops()?;
println!("Keyframes: {}, Avg GOP size: {:.1}", gop.keyframes.len(), gop.average_gop_size);

VFR Detection

use unbundle::MediaUnbundler;

let mut unbundler = MediaUnbundler::open("input.mp4")?;
let vfr = unbundler.video().analyze_vfr()?;
println!("VFR: {}, mean FPS: {:.2}", vfr.is_vfr, vfr.mean_fps);

Packet Inspection

use unbundle::MediaUnbundler;

let mut unbundler = MediaUnbundler::open("input.mp4")?;
for pkt in unbundler.packet_iter()? {
    let pkt = pkt?;
    println!("stream={} pts={:?} size={} key={}",
        pkt.stream_index, pkt.pts, pkt.size, pkt.is_keyframe);
}

API Documentation

Complete API documentation is available at docs.rs/unbundle.

Essential 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
FrameRange Specifies which frames to extract (range, interval, timestamps, etc.)
ExtractionConfig Configure threading, progress callbacks, cancellation, pixel format, resolution, HW accel

Stream & Iteration Types

Type Description
FrameIterator Lazy, pull-based frame iterator
AudioIterator Lazy pull-based audio sample iterator (mono f32)
AudioChunk A chunk of decoded audio samples with timing
PacketIterator Lazy raw-packet-level demuxer iterator
FrameStream Async stream of decoded frames via Tokio (feature: async-tokio)
AudioFuture Async audio extraction future (feature: async-tokio)

Configuration Types

Type Description
FrameOutputConfig Pixel format and resolution settings for frame output
PixelFormat Output pixel format (RGB8, RGBA8, GRAY8)
AudioFormat Output audio format (WAV, MP3, FLAC, AAC)
SubtitleFormat Output subtitle format (SRT, WebVTT, Raw)
ThumbnailConfig Grid thumbnail configuration (columns, rows, width)
GifConfig Animated GIF export configuration (width, delay, repeat) (feature: gif)
WaveformConfig Waveform generation settings (bin count, time range) (feature: waveform)
SceneDetectionConfig Scene detection threshold configuration (feature: scene-detection)
VideoWriterConfig Video writer settings (FPS, resolution, codec, CRF) (feature: video-writer)
HwAccelMode Hardware acceleration mode selection (feature: hw-accel)

Metadata Types

Type Description
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)
ChapterMetadata Chapter information (title, start/end times)
FrameInfo Per-frame decode metadata (PTS, keyframe flag, picture type)
FrameType Picture type enum (I, P, B, etc.)
KeyframeInfo Keyframe position metadata (packet number, PTS, timestamp)
GopInfo GOP structure analysis result (keyframes, sizes, statistics)
VfrAnalysis Variable frame rate detection result (min/max/mean FPS)
PacketInfo Per-packet metadata (stream index, PTS, DTS, size, keyframe)

Utility Types

Type Description
MediaProbe Lightweight stateless media file probing
ThumbnailGenerator Thumbnail generation helpers (single, grid, smart)
Remuxer Lossless container format conversion
ValidationReport Result of media file validation
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
SubtitleEvent A single decoded subtitle event (text, start/end time)
BitmapSubtitleEvent A bitmap subtitle event with image and timing

Feature-Specific Types

Type Feature Description
Transcoder transcode Audio re-encoding builder (format, range, bitrate)
VideoWriter video-writer Encodes image sequences into video files
VideoCodec video-writer Supported video codecs (H.264, H.265, MPEG-4)
WaveformData waveform Generated waveform result with per-bin statistics
WaveformBin waveform Single waveform bin (min, max, RMS amplitude)
LoudnessInfo loudness Peak/RMS loudness with dBFS equivalents
SceneChange scene-detection Detected scene change with timestamp and score
HwDeviceType hw-accel Supported HW device types (CUDA, VAAPI, etc.)

Performance

unbundle is designed for efficiency in both single-file and batch processing scenarios:

  • Smart seeking — Uses FFmpeg's keyframe-based seeking. For sequential access (ranges, intervals), frames are decoded without redundant seeks.
  • Lightweight decoders — Each extraction call creates a fresh decoder. FFmpeg decoder creation is fast relative to actual decoding work.
  • Batch optimizationFrameRange::Specific sorts requested frame numbers and processes them in order to minimize seeks.
  • Memory-efficient streamingfor_each_frame and FrameIterator process frames one at a time without buffering entire frame sets.
  • Parallel extractionframes_parallel() (feature parallel) splits frames across rayon threads, each with its own demuxer for true parallelism.
  • Hardware acceleration — When enabled (feature hw-accel), attempts GPU-accelerated decoding with automatic fallback to software.
  • Correct stride handling — Properly handles FFmpeg's row padding when converting frames to image buffers.
  • Zero-copy audio — Uses avio_open_dyn_buf for in-memory audio encoding without temporary files.

Testing

Generate test fixtures first:

# Linux / macOS
bash tests/fixtures/generate_fixtures.sh

# Windows
tests\fixtures\generate_fixtures.bat

Then run tests:

cargo test --all-features

Test Coverage

The test suite includes comprehensive coverage:

Test Module 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
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
audio_iter AudioIterator, chunk iteration, sample rates
keyframe_analysis GopInfo, KeyframeInfo, GOP statistics
vfr_analysis VfrAnalysis, constant vs variable frame rate
packet_iter PacketIterator, PacketInfo, stream filtering
subtitle_search Subtitle search, case-insensitive matching
metadata_extended Extended metadata: video tracks, colorspace, HDR

Feature-specific tests (require corresponding features enabled):

Test Module Feature Required Coverage
scene_detection scene-detection Scene change detection, threshold configuration
async_extraction async-tokio FrameStream, AudioFuture, async streaming
parallel_extraction parallel frames_parallel, sequential parity, interval mode
hw_accel hw-accel HW device enumeration, Auto/Software modes
gif_export gif GIF encoding, file and in-memory output
waveform waveform WaveformConfig, bin statistics, time ranges
loudness loudness Peak/RMS loudness, dBFS values
video_writer video-writer VideoWriter, codec selection, frame encoding
transcode transcode Transcoder, format conversion, time ranges

Benchmarks

Run performance benchmarks:

cargo bench --all-features

Criterion benchmarks are located in benches/ and measure:

  • Frame extraction throughput (single vs parallel)
  • Seek performance (sequential vs random access)
  • Audio extraction speed across formats
  • Iterator overhead vs batch extraction

Troubleshooting

FFmpeg Linking Errors

Problem: error: linking with 'cc' failed or cannot find -lavcodec

Solution:

  • Ensure FFmpeg development libraries are installed (see Installation)
  • Set PKG_CONFIG_PATH to point to FFmpeg's .pc files
  • On Windows, set FFMPEG_DIR environment variable
  • Verify with: pkg-config --libs --cflags libavcodec

Codec Not Supported

Problem: UnbundleError::CodecNotSupported

Solution:

  • Check that your FFmpeg build includes the required codec
  • Run ffmpeg -codecs to list available codecs
  • Some codecs require FFmpeg to be built with specific flags (e.g., --enable-libx264)

Hardware Acceleration Fails

Problem: Hardware decoding falls back to software or fails entirely

Solution:

  • Verify GPU drivers are up to date
  • Check available HW devices: HwDeviceType::available_devices()
  • Use ExtractionConfig::with_hw_accel(HwAccelMode::Auto) for automatic fallback
  • Not all codecs/formats support hardware acceleration
  • Try ffmpeg -hwaccels to list available hardware acceleration methods

Out of Memory

Problem: High memory usage when extracting many frames

Solution:

  • Use streaming iteration instead of batch extraction: frame_iter() or for_each_frame()
  • Process frames in smaller batches
  • Use AudioIterator for large audio files instead of loading entire tracks

Slow Frame Extraction

Problem: Frame extraction is slower than expected

Solution:

  • Use frames_parallel() (feature parallel) for CPU-bound workloads
  • Enable hardware acceleration (feature hw-accel) for high-resolution video
  • Avoid extracting specific frames in random order — sorted access is much faster
  • Consider using FrameRange::Interval instead of many individual frame numbers

Permission Denied / File Not Found

Problem: Cannot open media file

Solution:

  • Verify file path is correct and file exists
  • Check file permissions (readable by current user)
  • Ensure file is not locked by another process
  • On Windows, use raw string literals for paths: r"C:\path\to\video.mp4"

Contributing

Contributions are welcome! Please see the GitHub repository for:

  • Bug reports and feature requests
  • Pull requests
  • Discussions and questions

License

MIT — see LICENSE file for details.