ff-filter 0.15.0

Video and audio filter graph operations - the Rust way
Documentation
# ff-filter

Apply video and audio transformations without writing FFmpeg filter-graph strings. Build a chain with method calls; the graph description is generated and validated internally.

> **Project status (as of 2026-06-04):** The library foundation is in place. Development continues through [**avio-editor-demo**]https://github.com/itsakeyfut/avio-editor-demo, a real-world video editing application built on `avio`, which surfaces bugs and drives API improvements. Pull requests, bug reports, and feature requests are welcome — see the [main repository]https://github.com/itsakeyfut/avio for full context.

## Installation

```toml
[dependencies]
ff-filter = "0.15"
```

## Building a Filter Chain

```rust
use ff_filter::{FilterGraph, ScaleAlgorithm};

let graph = FilterGraph::builder()
    .trim(10.0, 30.0)                           // keep seconds 10–30
    .scale(1280, 720, ScaleAlgorithm::Fast)     // resize to 720p
    .fade_in(0.0, 0.5)                           // 0.5-second fade in at the start
    .fade_out(19.5, 0.5)                         // 0.5-second fade out
    .build()?;
```

`build()` checks the configured steps and returns an `Err` if a value is out of range or the step set is empty. The underlying `FFmpeg` graph itself is constructed lazily on the first `push_video` / `push_audio` call, using the first frame's format.

## Available Video Operations

This is a representative selection; see the API docs for the full set
(colour grading, blurs, denoise, keying, transitions, text, and more).

| Method                            | Effect                                               |
|-----------------------------------|------------------------------------------------------|
| `trim(start, end)`                | Discard frames outside the given time range (secs)   |
| `scale(w, h, algorithm)`          | Resize frames using the given resampling algorithm   |
| `fit_to_aspect(w, h, color)`      | Scale to fit `w × h`, preserving aspect (letterbox)  |
| `crop(x, y, w, h)`                | Extract a rectangular region                         |
| `overlay(x, y)`                   | Composite a second video stream at (x, y)            |
| `fade_in(start, duration)`        | Fade from black, starting at `start` (secs)          |
| `fade_out(start, duration)`       | Fade to black, starting at `start` (secs)            |
| `rotate(degrees, fill_color)`     | Rotate clockwise; exposed corners filled with color  |
| `tone_map(ToneMap::Hable)`        | HDR-to-SDR tone mapping with the selected curve      |

## Available Audio Operations

| Method                   | Effect                                            |
|--------------------------|---------------------------------------------------|
| `volume(gain_db)`        | Adjust loudness by the given number of dB         |
| `equalizer(bands)`       | Apply a multi-band parametric EQ (`Vec<EqBand>`)  |
| `amix(inputs)`           | Mix multiple audio streams into one               |

## Hardware Acceleration

```rust
use ff_filter::{FilterGraph, HwAccel, ScaleAlgorithm};

let graph = FilterGraph::builder()
    .scale(1920, 1080, ScaleAlgorithm::Fast)
    .hardware(HwAccel::Cuda)
    .build()?;
```

`HwAccel` selects the device type (`Cuda`, `VideoToolbox`, or `Vaapi`). When
hardware is enabled, `hwupload` / `hwdownload` filters are inserted around the
chain automatically.

## Using the Filter Graph

```rust
// Push decoded frames into input slot 0 and pull transformed frames out.
while let Some(input_frame) = decoder.decode_frame()? {
    graph.push_video(0, &input_frame)?;
    while let Some(output_frame) = graph.pull_video()? {
        encoder.push_video(&output_frame)?;
    }
}
```

Multi-input filters (such as `xfade` or `overlay`) read from additional slots:
push clip A frames to slot 0 and clip B frames to slot 1.

## Error Handling

| Variant                          | When it occurs                                             |
|----------------------------------|------------------------------------------------------------|
| `FilterError::InvalidConfig`     | A configured value is out of range (returned by `build()`) |
| `FilterError::BuildFailed`       | No steps were added, or the `FFmpeg` graph cannot be built |
| `FilterError::InvalidInput`      | A frame was pushed to an out-of-range input slot           |
| `FilterError::ProcessFailed`     | A push or pull operation failed                            |
| `FilterError::Ffmpeg`            | An underlying `FFmpeg` function returned an error code     |
| `FilterError::CompositionFailed` | A multi-track composition or mixing operation failed       |
| `FilterError::AnalysisFailed`    | An analysis operation (e.g. loudness measurement) failed   |

## MSRV

Rust 1.93.0 (edition 2024).

## License

MIT OR Apache-2.0