Video editing from Java, 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 Java usually costs
- A binary in the container image, which grows the image and puts a native dependency into a deployment that had none.
- The wrappers that bundle FFmpeg for you ship one build per platform, so the size comes back with a different name.
- ProcessBuilder hands you an exit code and a stream of stderr, so a wrong argument becomes a string to parse rather than a validation failure.
Trim a clip and wait for the file
Java
import java.net.URI;
import java.net.http.*;
import java.time.Duration;
public class Kinopipe {
private static final String BASE = "https://kinopipe.com/api/v1";
private static final HttpClient CLIENT = HttpClient.newHttpClient();
private static final String KEY = System.getenv("KINOPIPE_API_KEY");
static String send(HttpRequest.Builder builder) throws Exception {
HttpRequest request = builder
.header("Authorization", "Bearer " + KEY)
.header("Content-Type", "application/json")
.timeout(Duration.ofSeconds(30))
.build();
return CLIENT.send(request, HttpResponse.BodyHandlers.ofString()).body();
}
public static void main(String[] args) throws Exception {
String body = """
{
"inputs": [{ "id": "main", "url": "https://example.com/interview.mp4" }],
"options": { "start": 12, "end": 48 }
}
""";
String started = send(HttpRequest.newBuilder(URI.create(BASE + "/tools/trim-video"))
.POST(HttpRequest.BodyPublishers.ofString(body)));
String jobId = started.split("\"jobId\":\"")[1].split("\"")[0];
while (true) {
String job = send(HttpRequest.newBuilder(URI.create(BASE + "/jobs/" + jobId)).GET());
if (job.contains("\"status\":\"succeeded\"") || job.contains("\"status\":\"failed\"")) {
System.out.println(job);
return;
}
Thread.sleep(2000);
}
}
}- java.net.http is in the JDK from 11, so nothing above needs a dependency. Use Jackson or Gson rather than splitting strings once this is more than an example.
- 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
Why not ProcessBuilder and a local FFmpeg?
It works when you own the machine and accept the native dependency. The trade is an image that carries a binary and errors that arrive as text on stderr instead of as validation before the render.
Is there a Java SDK?
Not yet. The surface is a bearer token, one POST per operation and a GET to poll, which is why the example above needs nothing but the JDK.
Can I run several edits in parallel?
Yes. Jobs are independent, so submit them from a thread pool and poll each. Credits are reserved per job and refunded when a render fails.