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
ffmpeg -i input.mp4 -c:v libx264 -crf 28 -preset medium -c:a aac -b:a 128k -movflags +faststart output.mp4| Flag | What it does |
|---|---|
-c:v libx264 | Encode the video as H.264, which plays almost everywhere. |
-crf 28 | Constant 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 medium | Encode speed. slow and slower make smaller files at the same CRF; fast and veryfast finish sooner. |
-c:a aac -b:a 128k | Encode the audio as AAC at 128 kbps. Use -c:a copy to keep the original audio. |
-movflags +faststart | Put 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:
{ "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:
{ "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:
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:
# Target: 50 MB for a 300-second video, 128 kbps audio# Total kbps = 50 * 8192 / 300 ≈ 1365; video = 1365 - 128 ≈ 1237kffmpeg -y -i input.mp4 -c:v libx264 -b:v 1237k -pass 1 -an -f mp4 /dev/nullffmpeg -i input.mp4 -c:v libx264 -b:v 1237k -pass 2 -c:a aac -b:a 128k output.mp4Aim 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
| Error | Cause | Fix |
|---|---|---|
height not divisible by 2 | A 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 input | The 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.
Related
- Convert MOV to MP4 with FFmpeg.
- FFmpeg cheat sheet: the common commands, each as an API call.
- Compress recipe reference.
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.