All posts
ffmpeg compress video

How to Compress Video with FFmpeg (Locally and with an API)

Compress video with FFmpeg: the CRF and preset flags that matter, how to hit a target file size, and the same job as one API call with no FFmpeg install.

To compress a video with FFmpeg, re-encode it with H.264 and a higher CRF value: ffmpeg -i input.mp4 -c:v libx264 -crf 28 -preset medium -c:a aac -b:a 128k output.mp4. A higher CRF gives a smaller file; a slower preset gives a smaller file at the same quality but takes longer.

Below are the flags explained, the variants for size, speed and resolution, the same job as one Xora API call, and the errors you are most likely to see. For other common commands, see the FFmpeg cheat sheet.

The local command

Terminal window
ffmpeg -i input.mp4 -c:v libx264 -crf 28 -preset medium -c:a aac -b:a 128k -movflags +faststart output.mp4
FlagWhat it does
-c:v libx264Encode the video as H.264, which plays almost everywhere.
-crf 28Constant rate factor: the quality target. Lower is better quality and a bigger file. 23 is the default; 18 to 28 is the usual range.
-preset mediumEncode speed. slow and slower make smaller files at the same CRF; fast and veryfast finish sooner.
-c:a aac -b:a 128kEncode the audio as AAC at 128 kbps. Use -c:a copy to keep the original audio.
-movflags +faststartPut the MP4 index at the start, so playback in a browser starts before the download ends.

The same job with the Xora API

The compress recipe sets the H.264 flags for you. You choose the CRF:

POST /v1/jobs — compress recipe
{
"mode": "recipe",
"input": { "url": "https://example.com/input.mp4" },
"output": { "format": "mp4" },
"recipe": { "name": "compress", "crf": 28 }
}

To keep full control of the flags, send the same arguments you run locally:

POST /v1/jobs — raw args
{
"mode": "ffmpeg",
"input_files": { "in_1": "https://example.com/input.mp4" },
"output_files": { "out_1": "output_crf28.mp4" },
"ffmpeg": {
"args": [
"-i", "{{in_1}}",
"-c:v", "libx264", "-crf", "28", "-preset", "medium",
"-c:a", "aac", "-b:a", "128k",
"-movflags", "+faststart",
"{{out_1}}"
]
}
}

Send either body to https://api.xora.sh/v1/jobs with Authorization: Bearer YOUR_API_KEY. The response is { id, state }; poll GET /v1/jobs/{id} or pass webhookUrl, and download the result from the signed URL. See the compress recipe for all options.

Variants

Smaller file: lower the resolution too

Fewer pixels compress much better than a higher CRF alone. Scale a 4K or 1080p source to 720p:

Terminal window
ffmpeg -i input.mp4 -vf scale=-2:720 -c:v libx264 -crf 26 -preset medium -c:a aac -b:a 96k output_720p.mp4

-2 keeps the width an even number, which H.264 needs. With Xora, add "-vf", "scale=-2:720" to the args, or use the resize recipe first.

A target file size: two-pass encoding

CRF gives a constant quality, not a constant size. When the size must be under a limit (an upload cap, for example), calculate the bitrate and use two passes:

Terminal window
# Target: 50 MB for a 300-second video, 128 kbps audio
# Total kbps = 50 * 8192 / 300 ≈ 1365; video = 1365 - 128 ≈ 1237k
ffmpeg -y -i input.mp4 -c:v libx264 -b:v 1237k -pass 1 -an -f mp4 /dev/null
ffmpeg -i input.mp4 -c:v libx264 -b:v 1237k -pass 2 -c:a aac -b:a 128k output.mp4

Aim a little under the limit, because the container adds a small overhead.

Faster encodes

Use -preset fast or -preset veryfast. The file is bigger at the same CRF, so raise the CRF by one or two to compensate.

Screen recordings

Screen recordings have large flat areas and compress very well. -tune stillimage can help for slides; a higher CRF (30 or more) often still looks sharp.

Common errors

ErrorCauseFix
height not divisible by 2A scale filter gave an odd size.Use -2 for the automatic side: scale=-2:720.
Unknown encoder 'libx264'The local FFmpeg build has no x264.Install a full build, or run the job on Xora.
The output is bigger than the inputThe source was already well compressed, and the CRF is too low.Raise the CRF, lower the resolution, or keep the original.
VALIDATION_ERROR (Xora)A field in the job body is wrong.Read error.message; it names the field.
LIMIT_EXCEEDED (Xora)The plan’s credits are used up.Wait for the monthly reset or change plan.

Failed and rejected Xora jobs cost nothing. See error handling.

Frequently asked questions

What is the best FFmpeg command to compress a video?

ffmpeg -i input.mp4 -c:v libx264 -crf 28 -preset medium -c:a aac -b:a 128k output.mp4 is a good start. CRF sets the quality: higher numbers give smaller files. Try values between 23 and 30 and keep the highest one that still looks good to you.

How do I compress a video to a specific file size with FFmpeg?

Calculate a bitrate from the size and the duration, and use a two-pass encode. For example, 50 MB for a 5-minute video is about 1,333 kbps in total; take off the audio bitrate and pass the rest as -b:v in both passes.

Does compressing with FFmpeg reduce quality?

Re-encoding is lossy, so some detail is always lost. At a moderate CRF the loss is hard to see, and the file can be much smaller, more so when the source was recorded at a high bitrate, such as a phone or screen recording.

Can I compress a video without installing FFmpeg?

Yes. Send the video URL to the Xora API with the compress recipe and a CRF value, or send your own FFmpeg arguments in mode ffmpeg. Xora runs FFmpeg in the cloud and returns a signed URL for the smaller file.

Ready to try Xora?

Replace your local FFmpeg binaries with a secure, high-performance cloud API.