A safe, high-level Rust wrapper around FFmpeg for building video editors — decode, encode, filter, compose, and stream.
First of all, thank you for finding this project. I would like to briefly share the current state of things.
AI agents have been evolving remarkably in recent times, and large projects such as Rust and Bevy have declared any issue, comment, or code generated by AI to be a violation of their contribution guidelines. I believe this is because whether copyright can apply to AI-generated content — and whether the engineer who directed the AI can bear full accountability — are still actively being debated. For now, most projects are erring on the side of caution and treating AI contributions as guideline violations.
At the same time, when an engineer designs the structs, traits, functions, variables, and overall architecture — deciding how to separate module responsibilities, how to connect them, which APIs to expose, and what to test — and then gives detailed instructions to an AI to generate code efficiently, the result is a dynamic where the engineer does the thinking and the AI does the work. I find this analogous to the relationship between an architect and a mid-level engineer. Of course, being the architect requires commensurate experience, technical depth, and judgment — but the structure of the relationship is similar.
Current phase (as of 2026-06-10): The library foundation is in place. Active development continues through avio-editor-demo — a real-world video editing application built on avio — which remains the primary vehicle for surfacing bugs and driving API improvements. Pull requests are welcome. While the contributor base is small, or whenever rapid iteration is needed, AI assistance may be used to accelerate development — always with the engineer in the architectural role, reviewing and taking responsibility for every change. Questions, bug reports, and feature requests are equally welcome.
The goal of this project is to serve as the core foundation for video delivery services and video editing applications built in Rust — not to cover every FFmpeg feature. The fundamental motivation was simply a desire to build a video editing application in Rust.
New contributors are very welcome — see CONTRIBUTING, and look for issues labeled good first issue or help wanted to get started.
If you have any questions or suggestions, I would be happy to hear them. Feel free to ask.
avio is a family of Rust crates for building video-editing and media applications — a safe, ergonomic toolkit over FFmpeg. It spans low-level decode/encode up through timeline composition, real-time preview, and GPU rendering. All public APIs are safe — unsafe internals are fully encapsulated, so you never need to write unsafe code in your application.
use ff_probe::open;
use ff_decode::VideoDecoder;
use ff_encode::{VideoEncoder, VideoCodec, AudioCodec, BitrateMode};
// Inspect a media file
let info = open("input.mp4")?;
if let Some(v) = info.primary_video() {
println!("{}x{} @ {:.2} fps", v.width(), v.height(), v.fps());
}
// Decode frames
let mut decoder = VideoDecoder::open("input.mp4").build()?;
while let Some(frame) = decoder.decode_one()? {
// process frame.planes() ...
}
// Re-encode
let mut encoder = VideoEncoder::create("output.mp4")
.video(1920, 1080, 30.0)
.video_codec(VideoCodec::H264)
.bitrate_mode(BitrateMode::Crf(23))
.audio(48000, 2)
.audio_codec(AudioCodec::Aac)
.build()?;
encoder.finish()?;| Crate | Description | crates.io | docs.rs |
|---|---|---|---|
ff-probe |
Media metadata extraction | ||
ff-decode |
Video and audio decoding | ||
ff-encode |
Video and audio encoding | ||
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 | ||
avio |
Facade crate — re-exports all member crates |
- Safe API — No
unsafecode required in user code - Probe — Extract metadata (codec, resolution, duration, bitrate, HDR info) from any media file
- Decode — Frame-by-frame video/audio decoding with Iterator pattern, seeking, and thumbnail generation
- Encode — Video/audio encoding with hardware acceleration and LGPL-compliant defaults
- Hardware Acceleration — NVENC/NVDEC, Intel QSV, AMD AMF, Apple VideoToolbox, VA-API
- Filter Graph — Trim, scale, crop, overlay, and more via
libavfilter - HLS/DASH Streaming — Adaptive bitrate output via
ff-stream - Async — Tokio-backed async decode/encode with back-pressure (
tokiofeature) - Cross-platform — Windows, macOS, Linux
Add the crates you need to your Cargo.toml:
[dependencies]
ff-probe = "0.15"
ff-decode = "0.15"
ff-encode = "0.15"
# Or use the facade crate for everything
avio = "0.15"FFmpeg 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-devuse ff_probe::open;
let info = open("video.mp4")?;
if let Some(video) = info.primary_video() {
println!("{}x{} @ {:.2} fps ({:?})", video.width(), video.height(), video.fps(), video.codec());
}
if let Some(audio) = info.primary_audio() {
println!("{} Hz, {} ch ({:?})", audio.sample_rate(), audio.channels(), audio.codec());
}use ff_decode::{VideoDecoder, AudioDecoder, SeekMode};
use ff_format::{PixelFormat, SampleFormat};
use std::time::Duration;
// Video
let mut decoder = VideoDecoder::open("video.mp4")
.output_format(PixelFormat::Rgba)
.build()?;
while let Some(frame) = decoder.decode_one()? {
// frame.planes() contains pixel data
}
// Seek and decode a single frame
decoder.seek(Duration::from_secs(30), SeekMode::Exact)?;
let frame = decoder.decode_one()?;
// Audio
let mut decoder = AudioDecoder::open("audio.mp3")
.output_format(SampleFormat::F32)
.output_sample_rate(48000)
.build()?;
while let Some(frame) = decoder.decode_one()? {
// frame.planes() contains audio samples
}use ff_encode::{VideoEncoder, VideoCodec, AudioCodec, BitrateMode, Preset};
// Automatically selects an LGPL-compatible encoder (hardware or VP9/AV1 fallback)
let mut encoder = VideoEncoder::create("output.mp4")
.video(1920, 1080, 30.0)
.video_codec(VideoCodec::H264)
.bitrate_mode(BitrateMode::Crf(23))
.preset(Preset::Fast)
.audio(48000, 2)
.audio_codec(AudioCodec::Aac)
.build()?;
for frame in video_frames {
encoder.push_video(&frame)?;
}
encoder.finish()?;use ff_decode::{VideoDecoder, HardwareAccel};
use ff_encode::{VideoEncoder, HardwareEncoder};
// Decode with GPU
let decoder = VideoDecoder::open("video.mp4")
.hardware_accel(HardwareAccel::Auto)
.build()?;
// Encode with GPU
let encoder = VideoEncoder::create("output.mp4")
.video(1920, 1080, 60.0)
.hardware_encoder(HardwareEncoder::Auto)
.build()?;The best way to see avio in action is to run these projects. If either interests you,
contributions to them are very welcome too — they are where new requirements and rough edges
surface first.
ascii-term — Terminal ASCII Art Video Player
A full-featured terminal media player built entirely on avio. It renders video as colored
ASCII art in the terminal with synchronized audio playback, and was fully migrated from
ffmpeg-next / ffmpeg-sys-next to avio.
What it demonstrates:
VideoDecoderwithPixelFormat::Rgb24output for per-pixel luminance mappingAudioDecoderwith custom PCM conversion (SampleFormat::F32) feeding rodio for playback- Synchronized audio/video across two threads via
crossbeam-channel - 10 selectable ASCII character maps, per-character RGB coloring, real-time terminal resize
This is a real-world proof that avio can replace ffmpeg-next / ffmpeg-sys-next in
decode-heavy applications without any direct unsafe FFmpeg code in the application layer.
avio-editor-demo — Non-Linear Video Editor
A real-world non-linear video editor built on avio, and the primary driver of the library's
API. It exercises the full decode → timeline compose → real-time preview → export path, and
is where most bugs and API improvements originate.
What it demonstrates:
Timeline/Clipmulti-track composition with per-clip effects (colour correction, LUT, transitions)- Real-time monitor preview that matches the exported result (same
yuv420ppipeline) ff-previewproxy workflow andff-renderGPU compositing
Give it a try — and if you'd like to help shape a real Rust video editor, jump in.
| Platform | Status | Hardware Acceleration |
|---|---|---|
| Windows | ✅ | NVENC/NVDEC, QSV, AMF |
| macOS | ✅ | VideoToolbox |
| Linux | ✅ | VAAPI, NVENC/NVDEC, QSV |
Rust 1.93.0 or later (edition 2024).
Licensed under either of:
at your option.
This project links against FFmpeg, which is licensed under LGPL 2.1+ by default. The gpl feature of ff-encode enables GPL-licensed codecs (libx264, libx265) — see ff-encode for details.
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.