VidClip

FFmpeg Compress Video: The Command, Flag by Flag

By Mark Fulton · 2026-10-12 · 12 min read

FFmpeg Compress Video: The Command, Flag by Flag

The simplest FFmpeg command that reliably shrinks a video is ffmpeg -i input.mp4 -c:v libx264 -preset veryfast -crf 28 -pix_fmt yuv420p -c:a aac -b:a 128k -movflags +faststart output.mp4. It re-encodes the video as H.264 at a quality level you pick with -crf (higher number, smaller file), keeps the audio at 128 kbps AAC, and lays the file out so it plays before it finishes downloading. On the test clip I measured for this page, that exact command turned a 24.2 MB file into a 4.9 MB one. Below is what every flag does, three ways to go smaller, and the cases where compressing makes things bigger.

Most answers to this question hand you -crf 28 and stop. That works until it does not: the file comes out the same size, or you need exactly 8 MB, or the result will not play on a phone. Each of those has a specific flag behind it, so this page builds the command piece by piece and puts a measured number next to every choice.

How I measured the sizes on this page

Every size below comes from one clip, encoded on this machine with FFmpeg 8.1 (a full build). The clip is a synthetic one: 15 seconds of 1920x1080 at 30 fps, generated with FFmpeg's testsrc2 pattern plus a light layer of noise, and a 440 Hz tone for audio. I encoded it to H.264 at CRF 17 to stand in for a camera original, and the result is 24,205,407 bytes at about 12.9 Mbps overall.

That is a stand-in, not a phone recording. Real footage has faces, grass, and camera shake, and it compresses differently. Treat the numbers as a demonstration of how each flag moves the size relative to the others, and run the same commands on your own file to get your own numbers. Where the synthetic clip behaves oddly, I say so.

What is the simplest command that shrinks a video?

Here is the base command, the same recipe the VidClip compressor runs inside your browser tab:

ffmpeg -i input.mp4 -c:v libx264 -preset veryfast -crf 28 -pix_fmt yuv420p -c:a aac -b:a 128k -movflags +faststart output.mp4
Part What it does
-i input.mp4 The file to read
-c:v libx264 Encode the video as H.264, the codec every phone, browser and chat app plays
-preset veryfast How hard the encoder works to squeeze the result
-crf 28 The quality target. Higher is smaller and softer
-pix_fmt yuv420p Use 4:2:0 colour sampling so players like QuickTime and most TVs accept the file
-c:a aac -b:a 128k Re-encode audio as AAC at 128 kbps
-movflags +faststart Move the MP4 index to the front of the file
output.mp4 The result. Never the same name as the input

Measured result on the test clip: 4,896,155 bytes, about 20% of the original. That is a 4.9 MB file from a 24.2 MB one.

If you remember one thing: size is bitrate multiplied by duration, and the flags above are different ways of lowering the bitrate. The arithmetic is laid out in video compression by the numbers.

What do CRF and preset each control?

They control two different things, and mixing them up is the most common reason a command does not do what people expect.

CRF (constant rate factor) controls quality, and quality decides size. Instead of telling the encoder a bitrate, you tell it how good the picture should look, and it spends whatever bits each scene needs. For x264 the CRF guide from Werner Robitza puts sane values between 18 and 28 with a default of 23, and says a change of 6 should about halve or double the file size, though results vary. Lower is better quality and a bigger file.

Here is what I measured, same clip, same preset, only -crf changed:

CRF Size in bytes Share of original
18 20,998,082 87%
23 10,801,970 45%
28 4,896,155 20%
32 3,135,910 13%

From 18 to 23 the file roughly halved, and from 23 to 28 it more than halved again, so five steps did what the guide says six should do. Your footage will vary, which is the guide's point. Notice the shape: each step up saves fewer bytes than the one before, because there are fewer bytes left to save. CRF 32 is not twice as good as CRF 28. It is 36% smaller and visibly rougher on detailed footage. A reasonable starting point is the default of 23 for footage you care about and 28 for sharing.

Preset controls effort, not quality. The presets run from ultrafast to veryslow. A slower preset spends more CPU time searching for ways to represent the same picture in fewer bits. At the same CRF, the quality target is the same, and the file tends to get smaller as you go slower.

On this clip the pattern was messier than the textbook version:

Preset (CRF 28) Size in bytes
ultrafast 19,597,414
veryfast 4,896,155
medium 5,387,985
slow 5,441,859

Two lessons. First, ultrafast is a trap for compression: it nearly returned the original size, because it switches off most of the tools that find savings. It exists for live capture where speed matters more than size. Second, medium and slow came out slightly larger than veryfast here. I would not read that as a rule. This clip is a test pattern with noise, which has no real motion for a slower search to exploit, and CRF mode holds a quality target rather than a size, so a slower preset is allowed to spend bytes on fidelity at the same number. On real video, the slower presets usually earn their time, so try medium on your own file and compare. For a one-off compress where you will not wait around, veryfast is a good default.

Why add -pix_fmt yuv420p and -movflags +faststart?

Neither flag makes the file smaller. Both make the file work, which is what matters once you send it somewhere.

-pix_fmt yuv420p forces 4:2:0 chroma subsampling. If your source was recorded in a different pixel format (screen recorders and some cameras do this), FFmpeg's H.264 encoder can keep that format and produce a file that plays in VLC but shows a black frame or an error in QuickTime, some browsers and many TVs. Forcing yuv420p is cheap insurance.

-movflags +faststart fixes a layout problem. An MP4 stores its index (the "moov atom"), which tells a player where each frame is. By default that index is written at the end of the file, so a player has to download everything before it can start. The FFmpeg formats documentation describes faststart as running a second pass that moves the index to the beginning of the file, and notes that it can take a while and is not enabled by default. For video you will upload, embed or message, turn it on.

One more: -c:a aac -b:a 128k re-encodes the audio. On a 15 second clip that is roughly 240 KB, which is small, but on a long recording of a single voice you can trim more by dropping to -b:a 96k. If the audio is already AAC and you want to leave it alone, -c:a copy skips the re-encode.

How do you go smaller than the base command?

There are three levers. I measured all three against the 4.9 MB base.

Variant 1: smaller by CRF. Raise the number. -crf 32 took the clip to 3.1 MB. The trade is picture softness, worst in dark scenes, gradients and fast motion. Past about 32 you are in "readable, not nice" territory.

Variant 2: smaller by resolution. Add a scale filter before encoding:

ffmpeg -i input.mp4 -vf scale=-2:720 -c:v libx264 -preset veryfast -crf 28 -pix_fmt yuv420p -c:a aac -b:a 128k -movflags +faststart output.mp4

scale=-2:720 sets the height to 720 and lets FFmpeg compute the width, rounded to an even number (H.264 in 4:2:0 needs even dimensions, which is what the -2 is for). Measured: 1,384,958 bytes at CRF 28, and 2,535,479 bytes at CRF 23. That is about 6% and 10% of the original. Cutting the pixel count is a bigger lever than most CRF changes: the 720p file at CRF 23 (2.5 MB) is about half the size of the 1080p file at CRF 28 (4.9 MB), and on a phone screen it will often look as good. If the destination is a small screen, resize first. The crop versus resize post covers when each tool is the right one, and the resize tool does the same scale for you.

Variant 3: fit a size with bitrate and maxrate. CRF cannot be told "make it 2 MB." To land near a number, switch to an average bitrate with a cap:

ffmpeg -i input.mp4 -c:v libx264 -preset veryfast -b:v 1000k -maxrate 1000k -bufsize 2000k -pix_fmt yuv420p -c:a aac -b:a 128k -movflags +faststart output.mp4

The arithmetic: a 15 second clip at 1000 kbps video plus 128 kbps audio is 1128 kbps, and 1128 kbps x 15 s / 8 = about 2.1 MB. Measured: about 2.14 MB, which matches. FFmpeg's codec options documentation defines maxrate as the maximum bitrate tolerance and says it requires bufsize to be set. That is why the flags travel together: -b:v is the average you want, -maxrate stops the encoder spiking above it, and -bufsize is the window it averages over (twice the bitrate is a common starting point).

The formula for any target: take the size you need in megabytes, multiply by 8, divide by the duration in seconds, subtract the audio bitrate, and keep a margin of a few percent because container overhead and rate control are not exact. For uploads with a hard limit, aim 5 to 10 percent under. The full method, including two-pass encoding for tighter control, is in how to compress a video to an exact size.

Notice the trade: a bitrate target is blind to content. The same 1000 kbps that is generous for a talking head is starved for gameplay, so you get a predictable size and an unpredictable look. CRF is the opposite.

Does -c copy reduce file size?

No. -c copy copies the compressed streams from the input to the output without decoding them, so there is nothing to shrink. I ran ffmpeg -i src.mp4 -c copy copy.mp4 on the test clip and the output was 24,205,407 bytes, byte for byte the same size as the input.

That is exactly why -c copy is useful for other jobs: cutting a clip, changing the container, or removing a stream, all without a quality loss and in seconds. Cuts made this way snap to keyframes, which is explained in lossless video trimming and keyframes. But the moment your goal is "smaller," you have to re-encode, because size lives in the bitrate and only an encoder changes the bitrate.

When does compressing make the file bigger?

More often than you would think, and it is almost always one of these:

  • The source was already efficient. Re-encoding a file that is already at a low bitrate with a high-quality CRF spends bits it never had. On the test clip, -crf 12 produced 36,835,607 bytes, about 52% larger than the 24.2 MB original. CRF 18 barely saved anything (21.0 MB) because the source was encoded at CRF 17 to begin with.
  • The preset is ultrafast. 19.6 MB against a 4.9 MB veryfast result, same CRF.
  • The source is HEVC and you encode to H.264. HEVC is more efficient, so a straight swap to H.264 at a similar quality level yields a bigger file. This is a common surprise with modern iPhone recordings. There is a walkthrough in converting HEVC to H.264.
  • Audio you did not need. Rare on long clips, but on a short silent screen recording, a stereo track at 192 kbps can be a visible share of the file.
  • A low CRF on noisy footage. Grain, low light and sensor noise are expensive for any codec. CRF treats noise as detail worth keeping.

The quick check before you spend time: look at the source bitrate with ffprobe -v error -show_entries format=bit_rate -of default=nw=1 input.mp4. If the number is already in the low single digits of Mbps for a 1080p file, there is little left to remove, and your best levers are resolution and trimming dead seconds off the ends.

Can I do this without a terminal?

You can. The compressor on this site runs this same libx264 recipe through FFmpeg compiled to WebAssembly, with presets that map onto CRF 23, 28 and 32. The file is read into your browser's memory and processed on your own CPU, so there is no upload bar and no account. Free exports take up to 200 MB of input and carry a small corner watermark; Pro removes both and adds exact-target-size compression. If you want to compare it with a desktop tool first, see HandBrake versus FFmpeg, and for the rest of the commands the tools run, the FFmpeg commands cheat sheet lists them all.

FAQ

What CRF should I use in FFmpeg?

For x264, start at the default of 23. Use 18 to 20 when you care about quality and will not mind a big file, and 26 to 28 for sharing, where smaller matters more than perfection. The CRF guide calls 18 to 28 the sane range. On the 1080p test clip here, CRF 23 gave 10.8 MB, CRF 28 gave 4.9 MB, and CRF 32 gave 3.1 MB. Your footage will land differently, so test on a 10 second excerpt before committing to a long file.

Does -c copy reduce file size?

No. It copies the existing compressed streams without re-encoding, so the output is the same size as the input. On the test clip the copy was identical to the byte. To make a file smaller you have to re-encode with -c:v libx264 (or another encoder) and a higher CRF, a lower resolution, or a bitrate cap.

Is H.265 better for compressing with FFmpeg?

At matched visual quality, H.265 (HEVC) generally produces smaller files than H.264, but it is slower to encode and plays on fewer devices without extra work. Its CRF scale is not the same as x264's, so -crf 28 in libx265 is not the same quality as -crf 28 in libx264. On the test clip, libx265 at CRF 28 gave 7.2 MB against 4.9 MB for libx264 at CRF 28, which tells you only that the numbers are not interchangeable, not that one codec is worse. If you need the file to play everywhere, H.264 is the safe choice. The trade-offs are covered in H.264 versus H.265.

Why is my compressed file larger than the original?

Usually because the source was already at a low bitrate, or your settings ask for more quality than the source had. A very low CRF (the test clip went from 24.2 MB to 36.8 MB at CRF 12), the ultrafast preset, or converting from HEVC to H.264 will all do it. Check the source bitrate with ffprobe, raise the CRF, switch off ultrafast, and consider scaling down to 720p.


VidClip is a free set of video trimming, compression, and GIF conversion. Everything runs in your browser, nothing uploads.