Video editing from Rust, without FFmpeg on your machine.
Trim, resize, compress, convert, caption and transcribe by calling one HTTP endpoint per operation. Parameters are validated before anything renders, so a bad call fails as a typed error rather than a broken encode.
What running FFmpeg from Rust usually costs
- The maintained bindings wrap the C libraries, so building needs the FFmpeg development headers present on the build machine.
- That requirement follows you into cross-compilation and static linking, which are often the reasons Rust was chosen in the first place.
- Shelling out instead avoids the linking but puts a binary back into the image and turns errors into stderr to parse.
Trim a clip and wait for the file
Rust
use serde_json::{json, Value};
use std::{env, thread, time::Duration};
const BASE: &str = "https://kinopipe.com/api/v1";
fn main() -> Result<(), Box<dyn std::error::Error>> {
let key = env::var("KINOPIPE_API_KEY")?;
let client = reqwest::blocking::Client::new();
let started: Value = client
.post(format!("{BASE}/tools/trim-video"))
.bearer_auth(&key)
.json(&json!({
"inputs": [{ "id": "main", "url": "https://example.com/interview.mp4" }],
"options": { "start": 12, "end": 48 }
}))
.send()?
.json()?;
let job_id = started["jobId"].as_str().ok_or("no job id in the response")?;
loop {
let job: Value = client
.get(format!("{BASE}/jobs/{job_id}"))
.bearer_auth(&key)
.send()?
.json()?;
match job["status"].as_str() {
Some("succeeded") => {
println!("{}", job["result"]["output"]["downloadUrl"]);
return Ok(());
}
Some("failed") => return Err(job["errorMessage"].to_string().into()),
_ => thread::sleep(Duration::from_secs(2)),
}
}
}- Two crates, reqwest and serde_json, and no system library. The async version is the same code with await on each call.
- A split job returns every segment under outputs rather than output, which matters if you call split-video or split-video-by-scenes.
- Send an Idempotency-Key header when you retry, and the retry returns the original job instead of paying for a second render.
FAQ
What about ffmpeg-next?
It is the right tool when you need frame-level access in process and accept building against the C libraries. This is for the case where you only want the finished file.
Does this work in a scratch container?
Yes. Nothing native is linked and nothing is spawned, so the binary stays self-contained.
Can I run edits concurrently?
Yes. Jobs are independent, so drive them from tasks and poll each. Credits are reserved per job and refunded when a render fails.