Blog encoding
Target an exact video file size: two-pass encoding
Most encoders target quality, some bitrate, almost none target file size. How two-pass encoding works, when to use it, and the math behind hitting any cap.
If you’ve ever needed a video file under exactly 100 MB, you’ve felt the gap. Most encoders happily let you target a quality level (CRF 23), or a constant bitrate, or a maximum bitrate. Almost none let you say “give me a 100 MB file.” That problem is what two-pass encoding solves.
This post is the practical version: what it is, how the math works, when to use it, and how to do it without writing ffmpeg commands.
TL;DR
Two-pass encoding runs the video twice. The first pass analyzes content complexity; the second pass uses that to spend bits where they matter, producing better quality at a given target bitrate than single-pass.
For “give me a 100 MB file” workflows, two-pass is the only way to hit the target reliably. Video Forge automates both passes; if you want it manual, the ffmpeg pattern is at the end of this post.
The problem two-pass solves
Single-pass encoding makes bitrate decisions one frame at a time, with no knowledge of what’s coming next. If the encoder hits a complex scene late in the file and has already spent its budget on simpler earlier scenes, quality drops. If the file starts complex and gets simple, the encoder wastes bits on the simple back half.
Two-pass solves this by giving the encoder the whole picture before it makes any commits:
- Pass 1 runs through the video once, analyzing per-frame complexity, motion, and predictability. Output: a stats file (no actual video).
- Pass 2 uses the stats to allocate bitrate intelligently across the timeline. Output: the actual encoded video, hitting the target bitrate on average with much better visual quality than single-pass at the same bitrate.
The result is the encoder spending more bits on the fast-moving sports sequence and fewer on the static interview shot, and ending up at exactly (or very close to) the target file size.
The size-to-bitrate math
Every two-pass workflow starts with this conversion: take a desired file size and a duration, and compute the target average bitrate.
target_video_kbps = (max_size_mb × 1024 × 1024 × 8) / (duration_seconds × 1000)
− audio_kbps
× 0.95 # safety margin
The × 0.95 is to leave room for container overhead and the natural
variance of variable-bitrate output. Without it, you end up about 3 to 5%
over the target.
Worked example: target 100 MB, 5-minute video, AAC audio at 192 kbps:
total_kbps = (100 × 1024 × 1024 × 8) / (300 × 1000)
= 2796 kbps
video_kbps = 2796 − 192 = 2604 kbps
× 0.95 = ~2473 kbps
So a 5-minute video targeted at 100 MB needs ~2473 kbps of video bitrate. At 1080p with H.264, that’s just-good-enough quality. At 720p, it’s generous. At 4K, it’s going to look soft. Resolution choice is part of the budget.
When two-pass is worth it
Use two-pass when:
- You have a hard file-size cap (WhatsApp 16 MB, Discord 10 MB, Gmail 25 MB, uploads with a size budget).
- You’re producing for a fixed bitrate contract (broadcast spec, OTT delivery spec, archive disk allocation).
- The visible quality difference matters and the encode time doesn’t.
Skip two-pass when:
- You just want it to look good and you don’t care about exact file size. Use CRF instead — single-pass, half the time, often indistinguishable quality.
- You’re testing/iterating. Two-pass at every iteration is painfully slow. Use CRF for iteration, switch to two-pass for the final.
- Hardware acceleration is in play. NVENC and VideoToolbox can do two-pass but the quality lift is smaller because hardware encoders are already doing some lookahead.
A useful rule of thumb: if you’d be willing to ship the file at “whatever size CRF 23 gives me,” skip two-pass. If you’d be willing to ship at “exactly 50 MB, no more,” do two-pass.
How Video Forge handles it
In Video Forge, two-pass is automatic whenever you pick a destination with a size cap (WhatsApp, Discord, Email) or set Quality Mode to “Target file size” in Manual.
The flow:
- You drop in a video; Video Forge reads its duration via ffprobe.
- You pick WhatsApp (or set a manual target like “50 MB”).
- The app computes the target bitrate using the formula above. The 0.95 safety margin is baked in.
- The job runs as two ffmpeg invocations under the hood — pass 1 produces a stats file in a tempdir; pass 2 reads the stats and produces the output.
- Both passes report progress to the UI. Pass 1 is mapped to 0-50% of the progress bar; pass 2 is 50-100%.
If you tweak settings after the bitrate has been computed (changing audio codec, resolution, etc.), Video Forge recomputes the target before the job starts. There’s nothing to remember.
Doing it manually with ffmpeg
If you want to drive ffmpeg directly:
# Pass 1: analyze, no output (writes to ffmpeg2pass-0.log)
ffmpeg -y -i input.mov -c:v libx264 -b:v 2473k -pass 1 \
-an -f mp4 /dev/null
# Pass 2: encode using the stats from pass 1
ffmpeg -i input.mov -c:v libx264 -b:v 2473k -pass 2 \
-c:a aac -b:a 192k -movflags +faststart \
output.mp4
Notes:
- The
-pass 1 -an -f mp4 /dev/nullpattern is the standard. On Windows useNULinstead of/dev/null. - The log file is named
ffmpeg2pass-0.logby default. Use-passlogfileto set a custom path if you’re running multiple two-pass encodes in parallel. - Don’t put the log file in a shared directory if you’re encoding multiple videos concurrently — they’ll fight over the same stats file. Video Forge uses a unique tempdir per job for exactly this reason.
For H.265 (libx265) — useful when archive size matters more than universal compatibility, see the codec comparison for when to pick H.265 over H.264 — the syntax is slightly different:
ffmpeg -y -i input.mov -c:v libx265 -b:v 1500k \
-x265-params pass=1 -an -f mp4 /dev/null
ffmpeg -i input.mov -c:v libx265 -b:v 1500k \
-x265-params pass=2 -c:a aac -b:a 192k output.mp4
x265 takes its pass directive inside -x265-params instead of as a top-level
flag.
Common mistakes
- Forgetting to subtract audio. Pure video bitrate × duration is not total file size; you also have audio (~128 to 256 kbps) plus container overhead. Subtracting audio matters most on short clips where it’s a bigger fraction of total budget.
- Not allowing a safety margin. Real-world encoders overshoot by 2-5%.
Multiplying by 0.95 (or setting
-maxrateto the target) avoids ending up slightly over the cap. - Two-pass with constant-bitrate flags. Combining
-b:vwith-crfor-maxratewithout-bufsizegives ambiguous behavior. Pick one strategy. - Two-pass on a single short clip. A 5-second clip doesn’t benefit much from two-pass; the encoder didn’t have enough time to mis-budget in single-pass. Reserve it for clips over ~30 seconds.
A practical comparison
I encoded a 60-second 1080p clip (mixed-content footage, M2 Pro, ffmpeg 7.0) three ways to a 30 MB target:
| Method | Time | Final size | Visual quality (PSNR) |
|---|---|---|---|
| Single-pass CRF 23 | 14s | 28.4 MB | 41.8 dB |
Single-pass -b:v 4Mbps | 12s | 30.1 MB | 41.2 dB |
Two-pass -b:v 4Mbps | 23s | 29.7 MB | 42.6 dB |
The two-pass version hit the target more precisely and gained almost 1 dB of PSNR over single-pass at the same bitrate. CRF was indistinguishable from two-pass on the eye-test but missed the target by 5%. The choice depends on whether “30 MB-ish” or “exactly 30 MB” is what you need.
FAQ
What is two-pass encoding? Two-pass encoding runs the video twice. The first pass analyzes content complexity and writes a stats file. The second pass uses those stats to allocate bitrate intelligently across the timeline, producing better quality at the target bitrate than single-pass.
Is two-pass slower than single-pass? Roughly 1.6 to 1.8× the time of single-pass. The first pass is fast (analysis only, no real output); the second pass is full-rate.
When should I use two-pass encoding? When you need to hit an exact file size or bitrate budget. For CRF-targeted “just make it look good” encodes, single-pass is fine.
Can two-pass encoding hit an exact file size? It usually lands within a few percent of the target. Allow a 5% safety margin, or set the target slightly below the cap.
Does Video Forge do two-pass automatically? Yes, whenever you pick a destination with a size cap (WhatsApp, Discord, Email) or set Quality Mode to “Target file size” in Manual mode.
Video Forge handles two-pass encoding automatically for any destination with a size cap. 10 conversions are free on macOS and Windows; ffmpeg is bundled.