The Rust-native video editing engine, built on safe FFmpeg primitives.
A high-level API over FFmpeg with two layers: an editing engine (tracks, clips, keyframes, GPU compositing, undo/redo) on top of a family of composable, model-free primitive crates you can also use on their own. Application code never needs unsafe.
Status: avio is pre-1.0. The API is still evolving and may change between minor versions.
- Safe by default: every unsafe FFmpeg call is encapsulated, so application code never needs
unsafe. - Ergonomic: builder APIs, typed formats, and errors that carry human-readable context instead of raw FFmpeg return codes.
- Two layers: an opinionated editing engine on top of model-free FFmpeg primitives, so you can adopt the whole engine or depend on a single
ff-*crate (see Design Philosophy). - Focused: a foundation for video delivery services and video editing applications in Rust; it does not try to cover every FFmpeg feature.
Re-encode a video to H.264, reusing the source resolution and frame rate:
use ff_probe::open;
use ff_decode::VideoDecoder;
use ff_encode::{VideoEncoder, VideoCodec, BitrateMode};
fn main() -> Result<(), Box<dyn std::error::Error>> {
// Inspect the input and reuse its resolution and frame rate.
let info = open("input.mp4")?;
let video = info
.primary_video()
.ok_or("input.mp4 has no video stream")?;
let (width, height, fps) = (video.width(), video.height(), video.fps());
println!("{width}x{height} @ {fps:.2} fps");
// A decoder for the input, an encoder for the output.
let mut decoder = VideoDecoder::open("input.mp4").build()?;
let mut encoder = VideoEncoder::create("output.mp4")
.video(width, height, fps)
.video_codec(VideoCodec::H264)
.bitrate_mode(BitrateMode::Crf(23)) // 0-51, lower = higher quality
.build()?;
// Decode every frame and re-encode it.
while let Some(frame) = decoder.decode_one()? {
encoder.push_video(&frame)?;
}
encoder.finish()?; // flush buffered frames and finalize the file
Ok(())
}Several good options wrap FFmpeg in Rust. They sit at different levels, so the right one depends on what you are building.
| Library | Layer | Editing model | unsafe in your code |
Runtime dependency | Best for |
|---|---|---|---|---|---|
avio + ff-* |
High-level engine + composable primitives | Yes | None | Linked libav* | Video editing apps and delivery services |
ffmpeg-next |
Thin safe wrapper, near 1:1 with libav* | No | Some | Linked libav* | Full low-level control of the FFmpeg API |
ez-ffmpeg |
High-level, mirrors the FFmpeg CLI | No | None | Linked libav* | Transcoding and filter jobs close to the CLI |
video-rs |
High-level frame toolkit | No | None | Linked libav* | Reading and writing frames (CV / ML pipelines) |
ffmpeg-sidecar |
Wraps the ffmpeg binary (subprocess) |
No | None | ffmpeg executable |
Driving the CLI with a clean iterator API |
ffmpeg-the-third is an actively maintained fork of ffmpeg-next. For raw FFI bindings, see ffmpeg-sys-next or rusty_ffmpeg. For a non-FFmpeg media framework with its own editing layer, see the gstreamer bindings.
avio is the only one of these that ships an editing model: a timeline of tracks and clips, a per-clip effect stack with keyframes, and a preview that matches the exported result. If you do not need that model, the ff-* primitives underneath it are usable on their own for safe decode, encode, filter, stream, and GPU compositing, with no editing concepts imposed.
avio is two layers: an opinionated editing engine on top of model-free FFmpeg primitive crates. Adopt the whole engine, or reach for a single primitive.
avio commits to one editing model: tracks, a per-clip effect stack, keyframes, and compositing, with model-to-frame derivation and undo/redo history. If that model fits your app, depend on avio, drive Timeline / Clip, and get a preview that matches the exported result. The engine answers what to edit.
The ff-* crates handle execution (decode, encode, filter, composite one frame, stream) and know nothing about timelines, tracks, or edits. They are model-free by construction (the editing model lives only in avio, at the top of the dependency graph), so nothing forces avio's model on you. Each is usable on its own: build a different editing model (a node-graph compositor, a magnetic timeline), or just do safe Rust media plumbing (decode, encode, transcode, stream). The primitives answer how to execute. See ff-decode for a decode-only example.
Add the avio engine, or individual ff-* primitives:
[dependencies]
avio = "0.18"
# Or pick individual primitives, without the engine
ff-probe = "0.18"
ff-decode = "0.18"
ff-encode = "0.18"All crates share a single workspace version and are released together in lockstep; see Versioning. FFmpeg 7.x or 8.x development libraries must be installed on your system.
vcpkg install ffmpeg:x64-windows
$env:VCPKG_ROOT = "C:\vcpkg"brew install ffmpegsudo apt install libavcodec-dev libavformat-dev libavutil-dev libswscale-dev libswresample-devAPI documentation is on docs.rs/avio; each ff-* primitive is documented on its own docs.rs page (linked in Crates).
| Crate | Description | crates.io | docs.rs |
|---|---|---|---|
avio |
Editing engine: owns the editing model (Timeline/Clip, derivation, history) and depends on the primitives | ||
ff-probe |
Media metadata extraction | ||
ff-decode |
Video and audio decoding | ||
ff-analysis |
Media analysis (scene, silence, BPM, scopes) | ||
ff-encode |
Video and audio encoding | ||
ff-remux |
Stream-copy remux (trim, audio replace/extract/add) | ||
ff-filter |
Filter graph operations | ||
ff-pipeline |
Decode, filter, encode pipeline | ||
ff-stream |
HLS/DASH streaming output | ||
ff-preview |
Real-time A/V preview and proxy workflow | ||
ff-render |
GPU compositing pipeline (wgpu) | ||
ff-format |
Shared type definitions | ||
ff-common |
Common traits and buffer pooling | ||
ff-sys |
Low-level FFmpeg FFI bindings |
The editing engine (Timeline / Clip / Editor / render), media probing (open), and analysis are always present in avio; the flags add opt-in capabilities on top:
| Feature | Default | Enables |
|---|---|---|
hwaccel |
yes | Hardware-accelerated export (NVENC, QSV, AMF, VideoToolbox, VA-API) |
preview |
Real-time TimelinePlayer and Scene types |
|
serde |
serde (de)serialization of the editing model | |
gpl |
GPL-only codecs (libx264, libx265) |
For standalone primitive work (a raw decoder, encoder, pipeline, stream output, or the GPU compositor), depend on the ff-* crate directly; each carries its own feature flags (tokio, srt, render-gpu, and so on).
All crates in this repository (avio and the ff-* family) share one workspace version, defined in [workspace.package] in Cargo.toml, and are published together. Their version numbers always move in lockstep; the shared version is bumped only when the crates advance as a set.
| Platform | Status | Hardware acceleration |
|---|---|---|
| Windows | ✅ | NVENC/NVDEC, QSV, AMF |
| macOS | ✅ | VideoToolbox |
| Linux | ✅ | VAAPI, NVENC/NVDEC, QSV |
A terminal media player that renders video as colored ASCII art with synchronized audio. It was migrated from ffmpeg-next / ffmpeg-sys-next to avio, with no direct unsafe FFmpeg code in the application. It uses:
VideoDecoderwithPixelFormat::Rgb24for per-pixel luminance mappingAudioDecoderwith PCM conversion (SampleFormat::F32) feeding rodio- Synchronized audio and video across two threads via
crossbeam-channel
A non-linear video editor and the main driver of the library's API. It exercises the full decode, timeline compose, preview, and export path, and is where most bugs and API changes originate. It uses:
Timeline/Clipmulti-track composition with per-clip colour correction and transitions- A real-time preview that matches the exported result
- The
ff-previewproxy workflow, plus scene/silence detection, waveform, and EBU R128 loudness analysis
Pull requests, bug reports, and feature requests are welcome. See CONTRIBUTING, and look for issues labeled good first issue or help wanted. avio-editor-demo drives most API changes, so it is a good place to see what is needed next.
Rust 1.93.0 (edition 2024).
Dual-licensed under either MIT or Apache-2.0 at your option.
avio links against FFmpeg, which is LGPL 2.1+ by default. The gpl feature of ff-encode enables GPL-licensed codecs (libx264, libx265); see ff-encode.
The audio fixture used in integration tests is provided by Music Atelier Amacha (甘茶の音楽工房), composed by Amacha. Used with permission under the site's free-use terms.